jQuery AJAX: запросы $.ajax, $.get, $.post и их аналог на fetch

jQuery AJAX: запросы $.ajax, $.get, $.post и их аналог на fetch Полезное

jQuery AJAX — это набор функций библиотеки jQuery для отправки HTTP-запросов из JavaScript без перезагрузки страницы: базовая $.ajax() и сокращения $.get(), $.post(), $.getJSON(), .load(). Под капотом они работают через встроенный в браузер объект XMLHttpRequest и возвращают объект jqXHR, на который вешают обработчики .done(), .fail() и .always().

jQuery на 2026 год — библиотека для поддержки существующего кода: в новых проектах тот же запрос пишут на встроенном fetch. Поэтому ниже каждый прием показан в двух вариантах. Примеры проверены на jQuery 4.0.0 и Node.js 24 (сентябрь 2026).

Что есть что: AJAX, XHR, jqXHR, fetch

Эти слова часто смешивают, хотя они обозначают разные уровни.

Термин Что это Где живет
AJAX Подход: запрос из скрипта в фоне и обновление части страницы. Не библиотека и не протокол Идея, название с 2005 года
XMLHttpRequest (XHR) Встроенный браузерный API для HTTP-запросов, событийный Браузер
jqXHR Обертка jQuery над XHR с методами промиса (.done, .fail, .then) jQuery
fetch Современный встроенный API на промисах Браузер, Node.js 18+

Буква X в AJAX исторически означает XML, но сегодня почти всегда передают JSON. Сам запрос остается обычным HTTP: сервер не знает, что его отправил jQuery.

Минимальный рабочий пример

Нужны сервер и страница. Сервер ниже отдает список курсов по GET, принимает запись на курс по POST (только в формате JSON) и раздает статику. Положите jquery.min.js и index.html в папку public рядом с сервером.

// server.js - учебный сервер: node server.js, затем http://localhost:3000
const http = require("node:http");
const fs = require("node:fs");
const path = require("node:path");

const courses = [{ id: 1, title: "JavaScript Developer. Basic" }];

function sendJson(res, status, obj) {
  res.writeHead(status, { "Content-Type": "application/json; charset=utf-8" });
  res.end(JSON.stringify(obj));
}

http.createServer((req, res) => {
  if (req.method === "GET" && req.url === "/api/courses") {
    return sendJson(res, 200, courses);
  }
  if (req.url === "/api/slow") { // для проверки таймаута
    return setTimeout(() => sendJson(res, 200, { late: true }), 3000);
  }
  if (req.method === "POST" && req.url === "/api/signup") {
    let body = "";
    req.on("data", (chunk) => (body += chunk));
    return req.on("end", () => {
      const type = req.headers["content-type"] || "";
      if (!type.startsWith("application/json")) {
        return sendJson(res, 415, { error: "ожидаю JSON", got: type });
      }
      let data;
      try { data = JSON.parse(body); } catch { return sendJson(res, 400, { error: "битый JSON" }); }
      if (!data.email) return sendJson(res, 422, { error: "нет email" });
      sendJson(res, 201, { ok: true, email: data.email });
    });
  }
  // статика: index.html и jquery.min.js из папки public
  const file = req.url === "/" ? "index.html" : path.basename(req.url);
  fs.readFile(path.join(__dirname, "public", file), (err, content) => {
    if (err) return sendJson(res, 404, { error: "not found" });
    const type = file.endsWith(".js") ? "text/javascript" : "text/html; charset=utf-8";
    res.writeHead(200, { "Content-Type": type });
    res.end(content);
  });
}).listen(3000, () => console.log("http://localhost:3000"));

Страница делает два запроса: GET при загрузке и POST при отправке формы.

<!doctype html>
<html lang="ru">
<head><meta charset="utf-8"><title>jQuery AJAX</title></head>
<body>
  <ul id="courses"></ul>
  <form id="signup">
    <input name="email" value="student@example.com">
    <button>Записаться</button>
  </form>
  <p id="status"></p>

  <script src="/jquery.min.js"></script>
  <script>
    // 1. GET: получить JSON и вывести список
    $.ajax({ url: "/api/courses", method: "GET", dataType: "json", timeout: 5000 })
      .done(function (courses) {
        courses.forEach(function (c) {
          $("<li>").text(c.title).appendTo("#courses"); // .text(), не .html()
        });
      })
      .fail(function (jqXHR, textStatus) {
        $("#status").text("Ошибка загрузки: " + textStatus + " " + jqXHR.status);
      });

    // 2. POST: отправить форму как JSON без перезагрузки страницы
    $("#signup").on("submit", function (event) {
      event.preventDefault();
      $.ajax({
        url: "/api/signup",
        method: "POST",
        contentType: "application/json",
        data: JSON.stringify({ email: $(this).find("[name=email]").val() }),
        dataType: "json"
      })
        .done(function (data, textStatus, jqXHR) {
          $("#status").text(jqXHR.status + ": записан " + data.email);
        })
        .fail(function (jqXHR) {
          $("#status").text(jqXHR.status + ": " + (jqXHR.responseJSON ? jqXHR.responseJSON.error : jqXHR.statusText));
        })
        .always(function () {
          console.log("запрос завершен");
        });
    });
  </script>
</body>
</html>

Запустите node server.js и откройте http://localhost:3000. В списке появится «JavaScript Developer. Basic». После нажатия кнопки в абзаце будет 201: записан student@example.com, в консоли — «запрос завершен». Если стереть email и отправить снова, сработает .fail: 422: нет email. Страница при этом не перезагружается.

Открывать index.html двойным кликом (адрес file://) нельзя: запросы к /api/... уйдут не на сервер, и пример не заработает.

Разбор $.ajax: главные параметры

$.ajax() принимает объект настроек. Остальные функции jQuery для запросов — сокращенные вызовы той же $.ajax().

Параметр Что делает В примере
url Адрес запроса "/api/courses"
method HTTP-метод (старое имя — type, работает как синоним) "POST"
data Данные: объект кодируется как форма a=1&b=2, строка уходит как есть JSON.stringify(...)
contentType Заголовок Content-Type тела запроса. По умолчанию — форма "application/json"
dataType Как разобрать ответ: json, text, html, script "json"
timeout Сколько миллисекунд ждать до ошибки timeout 5000
headers Дополнительные заголовки, например CSRF-токен —

Важно различать contentType и dataType: первый описывает то, что вы отправляете, второй — как jQuery разберет ответ.

Обработчики: .done, .fail, .always и .then

.done(data, textStatus, jqXHR) срабатывает при коде 2xx и успешном разборе ответа. .fail(jqXHR, textStatus, errorThrown) — при коде 4xx/5xx, таймауте, обрыве сети и ошибке разбора. .always — в любом случае. jqXHR поддерживает и .then(), поэтому цепочки промисов тоже работают: $.getJSON("/api/courses").then(d => d.length) вернет 1.

Опции success, error и complete внутри объекта настроек по-прежнему поддерживаются. А вот одноименные методы у результата убрали в jQuery 3.0, и старый код падает на 4.0.0 так:

TypeError: $.ajax(...).success is not a function

Исправление — заменить .success() на .done(), .error() на .fail(), .complete() на .always().

Сокращения: $.get, $.post, $.getJSON, .load

Сокращение Что делает Эквивалент через $.ajax
$.get(url, data) GET, параметры в строку запроса method: "GET"
$.post(url, data) POST, тело как форма method: "POST"
$.getJSON(url) GET с разбором JSON dataType: "json"
$("#box").load(url) GET и вставка HTML ответа в элемент; если вторым аргументом передан объект, уходит POST dataType: "html" + .html()
$.getScript(url) Загружает и выполняет JS dataType: "script"

Сокращения удобны, пока хватает умолчаний. В привычной позиционной форме $.post(url, data, success, dataType) параметра contentType нет, и это главный источник ошибки ниже. Объектная форма $.post({ url, data, contentType: "application/json" }) его принимает, но это уже тот же $.ajax с method: "POST".

Типичная ошибка: JSON уходит как форма

Неверно: передать объект или JSON-строку без contentType.

$.post("/api/signup", JSON.stringify({ email: "b@example.com" }), null, "json")
  .fail(function (jqXHR) { console.log(jqXHR.status, jqXHR.responseText); });

Фактический результат на нашем сервере:

415 {"error":"ожидаю JSON","got":"application/x-www-form-urlencoded; charset=UTF-8"}

Тело ушло строкой JSON, но с заголовком формы, и сервер его отверг. Если бы в data был объект, jQuery закодировал бы его как email=b%40example.com — сервер получил бы тот же 415. Исправление — $.ajax (или объектная форма $.post) с contentType: "application/json" и JSON.stringify, как в основном примере: такой запрос возвращает 201.

Тот же код на fetch

fetch встроен в браузеры и в Node.js 18+, библиотека не нужна. Ключевое отличие: промис fetch отклоняется только при сетевой ошибке или отмене. Ответ 404 или 422 считается успешным, поэтому код ответа проверяют сами через response.ok.

// fetch-demo.mjs - те же запросы на fetch (Node 18+ или браузер)
const base = "http://localhost:3000"; // в браузере на той же странице - просто ""

async function getJson(url, options = {}) {
  const response = await fetch(base + url, {
    signal: AbortSignal.timeout(2000), // аналог timeout: 2000 в $.ajax
    ...options,
  });
  const data = await response.json().catch(() => null);
  if (!response.ok) { // fetch НЕ отклоняет промис на 4xx/5xx
    throw new Error(`${response.status}: ${data?.error ?? response.statusText}`);
  }
  return data;
}

const courses = await getJson("/api/courses");
console.log("GET:", courses.map((c) => c.title));

const created = await getJson("/api/signup", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ email: "student@example.com" }),
});
console.log("POST:", created);

for (const [url, options] of [
  ["/api/signup", { method: "POST", headers: { "Content-Type": "application/json" }, body: "{}" }],
  ["/api/slow", {}],
]) {
  try {
    await getJson(url, options);
  } catch (error) {
    console.log("Ошибка:", error.name, error.message);
  }
}

При запущенном сервере node fetch-demo.mjs выводит:

GET: [ 'JavaScript Developer. Basic' ]
POST: { ok: true, email: 'student@example.com' }
Ошибка: Error 422: нет email
Ошибка: TimeoutError The operation was aborted due to timeout

Без проверки response.ok запрос с 422 прошел бы как успешный: fetch вернул бы ok: false, status: 422 без исключения.

Поведение jQuery $.ajax fetch
Ответ 4xx/5xx Уходит в .fail Промис выполняется, проверять response.ok
Разбор JSON По dataType или заголовку ответа Явно: await response.json()
Отправка JSON contentType + JSON.stringify Заголовок + JSON.stringify
Таймаут Опция timeout, статус timeout AbortSignal.timeout(ms), ошибка TimeoutError
Отмена jqXHR.abort() AbortController
Зависимость Библиотека в полной сборке Встроен

Если не получилось

  • $.ajax is not a function. Подключена slim-сборка (jquery.slim.min.js), в ней нет AJAX, а в jQuery 4 — еще и $.Deferred. Нужна полная jquery.min.js.
  • .fail с parsererror и кодом 200. Указан dataType: "json", а сервер вернул HTML (часто страницу ошибки или логина). В V8 текст такой: SyntaxError: Unexpected token '<', "<!doctype "... is not valid JSON. Проверьте ответ во вкладке Network.
  • Код 0 и статус timeout или error. Сервер не ответил за время timeout, соединение оборвалось или запрос заблокирован. Смотрите консоль и Network.
  • Ошибка CORS в консоли. Запрос идет на другой домен, а сервер не разрешил его заголовком Access-Control-Allow-Origin. Чинится на сервере, не в jQuery.
  • Сервер получил пустое тело или 415. Не совпал формат: см. раздел о contentType выше.

Безопасность запросов

Ответ сервера выводите через .text(), а не .html(): строка из базы, вставленная как HTML, может выполнить чужой скрипт (XSS). По той же причине .load() и dataType: "script" применяйте только к своему серверу.

Для POST-запросов с сессионными cookie сервер должен проверять CSRF-токен, а клиент — передавать его, например через headers или один раз в $.ajaxSetup(). CORS — это не защита API: он лишь говорит браузеру, каким сайтам можно читать ответ, а запросы из curl или с сервера он не ограничивает.

Учебный сервер выше не ограничивает размер тела, не проверяет права и не пишет журнал. В production это нужно добавить.

Выводы

  • jQuery AJAX — обертка над XMLHttpRequest: $.ajax() как основа, $.get, $.post, $.getJSON, .load как сокращения.
  • Результат запроса обрабатывают через .done, .fail, .always или .then; методы .success() и .error() удалены в jQuery 3.0.
  • Чтобы отправить JSON, нужны contentType: "application/json" и JSON.stringify; $.post по умолчанию шлет форму.
  • В новом коде тот же запрос пишут на fetch, но проверяют response.ok сами: ответ 4xx/5xx промис не отклоняет.

Где применяется / связь с практикой

jQuery AJAX встречается в поддержке сайтов на CMS, админках и старых интерфейсах: разработчику нужно читать такой код, чинить его и постепенно переводить на fetch. Что умеет сама библиотека помимо запросов, разобрано в статье библиотека jQuery: описание и области применения.

Освойте тему на практике

Асинхронный JavaScript, промисы, async/await и работу с сетью системно проходят на курсе «JavaScript-разработчик. Базовый уровень». Бесплатно разобрать отдельные темы с преподавателями можно на открытых уроках Otus.

FAQ

Можно ли сделать синхронный запрос через async: false?
Технически опция есть, но синхронный XHR в основном потоке браузеры считают устаревшим: он замораживает страницу. Используйте .done или await.

Как показать прогресс загрузки файла?
Через XHR: в $.ajax передают функцию xhr, которая возвращает XMLHttpRequest с обработчиком upload.onprogress. У fetch стандартного события прогресса отправки нет.

Стоит ли убирать jQuery из работающего проекта ради fetch?
Только если есть причина: размер бандла, переход на фреймворк, удаление другого legacy. Переписывать запросы по одному безопасно — оба способа работают на одной странице.

OTUS Журнал
Скидка 5% 14-20 сентября на курсы (popup)