Ajax
AJAX
Что это такое
Ajax — это техника, которая позволяет обмениваться данными с сервером из уже открытой страницы и не перезагружать её целиком. Изначально название расшифровывалось как Asynchronous JavaScript and XML, однако XML давно уступил место JSON, а механизм запроса перешёл от XMLHttpRequest к API fetch. Сама идея при этом не изменилась: обновить из сети часть страницы, а всё остальное оставить на месте.
Почему это важно
Пока Ajax не было, каждое взаимодействие означало отправку формы и загрузку страницы: заметное мерцание, потерянная позиция прокрутки, поход на сервер ради одной галочки. Именно Ajax заставляет приложение ощущаться приложением, а не документом, который перезагружается. Вместе с тем он вводит класс проблем, которого у перезагружаемой страницы нет, — частичное состояние. Страница может находиться посреди запроса, запрос может тихо отказать, а два запроса — обогнать друг друга. Поэтому понимать саму технику важнее, чем помнить имя API.
Ключевые идеи
Страница обновляется, а не заменяется
Запрос получает данные, а код обновляет только ту часть дерева, которая изменилась; остальное он не трогает. В этом и выгода — не нужно заново рендерить шапку, позиция прокрутки сохраняется, — и в этом же источник сложности: DOM и данные сервера теперь могут расходиться.
async function refreshTotals() {
const response = await fetch("/api/cart/totals");
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const totals = await response.json();
document.querySelector("[data-total]").textContent = formatMoney(totals.total);
}
fetch завершается успешно, даже когда запрос не удался
Промис отклоняется при сетевом сбое, а вот ответ 404 или 500 завершается успешно, со ok, установленным в false. Принять завершившийся ответ за успех — самая частая ошибка в Ajax: HTML страницы с ошибкой разбирается как JSON, и сбой всплывает как сбивающая с толку ошибка разбора.
Просите JSON и проверяйте форму
Промис возвращает response.json(), ведь тело читается после прихода заголовков. Разобранное значение имеет тип any, поэтому ожидаемая форма так и остаётся предположением, пока её что-нибудь не проверит. Проверяйте на границе — для всего, что сложнее прототипа.
const data = await response.json();
if (typeof data?.total !== "number") throw new Error("unexpected payload");
Отправляйте данные правильным методом и типом
Заголовок Content-Type: application/json обязателен для POST с телом JSON; отправка формы должна использовать FormData без заголовка, чтобы границу задал браузер. Ошибка здесь даёт запрос, который сервер не может разобрать, и обычно это выглядит как пустое тело.
Деградируйте, когда скрипт не запускается
Запасной вариант для любого запроса, который скрипт не смог сделать, — ссылка, которая выполняет переход, и форма, которая отправляется. Построить разметку так, чтобы она работала без Ajax, а улучшения добавить уже потом — значит сохранить страницу рабочей и тогда, когда скрипт не загрузился или заблокирован.
Практика
Загрузите и отрисуйте список без перезагрузки
Сделайте кнопку, которая получает список, отрисовывает его на странице, а при сбое и пока запрос в полёте показывает отдельное сообщение.
Готово, когда: кнопка показывает отключённое состояние загрузки, на ответ 500 выводится сообщение об ошибке, а не пустой список, и при этом остальная часть страницы не перерисовывается.
Дайте форме рабочий запасной вариант
Возьмите форму, которая отправляется через Ajax, и убедитесь, что она работает с отключённым JavaScript, а затем проверьте, что улучшенный путь даёт тот же результат.
Готово, когда: путь без скрипта отправляется и отрисовывается корректно, а улучшенный путь обновляется на месте без перезагрузки.
Частые ошибки
- Предположение, что завершившийся
fetchозначает успех — тело ответа 404 разбирается как ожидаемая полезная нагрузка; исправить: проверяйтеresponse.okи бросайте исключение со статусом. - Отрисовка до завершения запроса — состояние загрузки подменяется пустым, и интерфейс выглядит сломанным; исправить: отрисовывайте три явных состояния: загрузка, готово и ошибка.
- Два запроса, которые обгоняют друг друга — медленный ранний ответ перезаписывает быстрый поздний; исправить: прерывайте предыдущий запрос или игнорируйте ответ, чей запрос уже не актуален.
- Забытый тип содержимого JSON при записи — сервер получает неразбираемое тело; исправить: задайте
Content-Type: application/jsonи сериализуйте черезJSON.stringify. - Страница, которая работает только с Ajax — заблокированный или упавший скрипт не оставляет ничего рабочего; исправить: начните с работающей ссылки или формы и улучшайте её.
Что читать дальше
- JavaScript (02-frontend/06-javascript) — язык и DOM, которые вы обновляете из ответа.
- Асинхронный JavaScript (02-frontend/06-javascript/03-asynchronous-javascript) — промисы, отмена и обработка ошибок в том объёме, который нужен этой технике.
- RESTful API (05-backend-data/06-restful-api) — какой формы должны быть эндпоинты, которые вы вызываете.