Синхронные и асинхронные запросы
XMLHttpRequest поддерживает как синхронную, так и асинхронную коммуникацию. Однако в общем случае асинхронные запросы следует предпочитать синхронным по причинам производительности.
Синхронные запросы блокируют выполнение кода, что приводит к "зависанию" экрана и неотзывчивому пользовательскому опыту.
Асинхронный запрос
Если вы используете асинхронный XMLHttpRequest, вы получаете обратный вызов, когда данные были получены. Это позволяет браузеру продолжать работать нормально, пока ваш запрос обрабатывается.
Пример: отправка файла в журнал консоли
Это самое простое использование асинхронного XMLHttpRequest.
const xhr = new XMLHttpRequest();
xhr.open("GET", "/bar/foo.txt", true);
xhr.onload = (e) => {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
console.log(xhr.responseText);
} else {
console.error(xhr.statusText);
}
}
};
xhr.onerror = (e) => {
console.error(xhr.statusText);
};
xhr.send(null);
Строка xhr.open задаёт true для своего третьего параметра, чтобы указать, что запрос должен обрабатываться асинхронно.
Затем мы создаём объект обработчика событий и назначаем его атрибуту запроса onload. Этот обработчик проверяет атрибут запроса readyState, чтобы определить, завершена ли транзакция; если она завершена, и HTTP-статус равен 200, обработчик выводит полученное содержимое. Если произошла ошибка, выводится сообщение об ошибке.
Вызов xhr.send фактически инициирует запрос. Обработчик обратного вызова вызывается всякий раз, когда состояние запроса меняется.
Пример: создание функции для чтения внешнего файла
В некоторых случаях необходимо прочитать много внешних файлов. Это стандартная функция, которая асинхронно использует объект XMLHttpRequest для изменения содержимого считанного файла в указанном слушателе.
function xhrSuccess() {
this.callback.apply(this, this.arguments);
}
function xhrError() {
console.error(this.statusText);
}
function loadFile(url, callback, ...args) {
const xhr = new XMLHttpRequest();
xhr.callback = callback;
xhr.arguments = args;
xhr.onload = xhrSuccess;
xhr.onerror = xhrError;
xhr.open("GET", url, true);
xhr.send(null);
}
Использование:
function showMessage(message) {
console.log(`${message} ${this.responseText}`);
}
loadFile("message.txt", showMessage, "New message!\n\n");
Подпись вспомогательной функции loadFile объявляет (i) целевой URL для чтения (через HTTP-запрос GET), (ii) функцию для выполнения при успешном завершении операции XHR и (iii) произвольный список дополнительных аргументов, которые передаются через объект XHR (через свойство arguments) в функцию обратного вызова успеха.
Сначала мы объявляем функцию xhrSuccess, вызываемую при успешном завершении операции XHR. Она, в свою очередь, вызывает функцию обратного вызова, указанную в вызове функции loadFile (в данном случае функцию showMessage), которая была назначена свойству объекта XHR. Дополнительные аргументы (если таковые имеются), переданные в вызов функции loadFile, "применяются" к выполнению функции обратного вызова. Функция xhrError вызывается, когда операция XHR не завершается успешно.
Мы сохраняем функцию обратного вызова успеха, переданную во втором аргументе функции loadFile, в свойстве callback объекта XHR. Начиная с третьего аргумента, все оставшиеся аргументы функции loadFile собираются (с использованием синтаксиса параметра rest), назначаются свойству arguments переменной xhr, передаются функции обратного вызова успеха xhrSuccess и, в конечном итоге, передаются функции обратного вызова (в данном случае showMessage), которая вызывается функцией xhrSuccess.
Вызов xhr.open задаёт true для своего третьего параметра, чтобы указать, что запрос должен обрабатываться асинхронно.
Наконец, xhr.send фактически инициирует запрос.
Пример: использование таймаута
Вы можете использовать таймаут, чтобы предотвратить зависание кода во время ожидания завершения чтения. Это делается путём установки значения свойства timeout объекта XMLHttpRequest, как показано в коде ниже:
function loadFile(url, timeout, callback, ...args) {
const xhr = new XMLHttpRequest();
xhr.ontimeout = () => {
console.error(`The request for ${url} timed out.`);
};
xhr.onload = () => {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
callback.apply(xhr, args);
} else {
console.error(xhr.statusText);
}
}
};
xhr.open("GET", url, true);
xhr.timeout = timeout;
xhr.send(null);
}
Обратите внимание на добавление кода для обработки события "таймаут" путём установки обработчика ontimeout.
Использование:
function showMessage(message) {
console.log(`${message} ${this.responseText}`);
}
loadFile("message.txt", 2000, showMessage, "New message!\n");
Здесь мы устанавливаем таймаут 2000 мс.
Синхронный запрос
Предупреждение: Синхронные запросы XHR часто приводят к зависанию веб-страницы, особенно при плохом сетевом соединении или медленном ответе удалённого сервера. Синхронные XHR устарели и следует избегать в пользу асинхронных запросов.
Все новые функции XHR, такие как timeout или abort, не допускаются для синхронных XHR. При попытке их использования будет вызвано исключение InvalidAccessError.
Пример: синхронный HTTP-запрос
Этот пример демонстрирует, как сделать простой синхронный запрос.
const request = new XMLHttpRequest();
request.open("GET", "/bar/foo.txt", false); // `false` makes the request synchronous
request.send(null);
if (request.status === 200) {
console.log(request.responseText);
}
Вызов request.send отправляет запрос. Параметр null указывает, что для запроса GET не требуется содержимое тела.
Выражение if проверяет код состояния после завершения транзакции. Если результат равен 200 — HTTP-результат "OK" — содержимое текста документа выводится в консоль.
Пример: синхронный HTTP-запрос из Worker
Один из немногих случаев, когда синхронный запрос обычно не блокирует выполнение, это использование XMLHttpRequest внутри Worker.
example.html (главная страница):
<!doctype html>
<html lang="en-US">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" />
<title>MDN Example</title>
<script>
const worker = new Worker("myTask.js");
worker.onmessage = (event) => {
alert(`Worker said: ${event.data}`);
};
worker.postMessage("Hello");
</script>
</head>
<body>
…
</body>
</html>
myFile.txt (цель вызова синхронного XMLHttpRequest):
Hello World!!
myTask.js (объект Worker):
self.onmessage = (event) => {
if (event.data === "Hello") {
const xhr = new XMLHttpRequest();
xhr.open("GET", "myFile.txt", false); // synchronous request
xhr.send(null);
self.postMessage(xhr.responseText);
}
};
Примечание: Эффект является асинхронным из-за использования Worker.
Эта модель может быть полезна, например, для взаимодействия с сервером в фоновом режиме или для предварительной загрузки содержимого. См. Использование веб-воркеров для примеров и подробностей.
Адаптация случаев использования синхронного XHR к API Beacon
Существуют случаи, когда синхронное использование XMLHttpRequest не заменяемо, например, во время событий unload, beforeunload и pagehide. В этих случаях следует рассмотреть использование API fetch() с флагом keepalive. Если fetch с keepalive недоступно, можно рассмотреть использование API navigator.sendBeacon(), который может поддерживать эти случаи, обычно обеспечивая хороший пользовательский опыт.
Следующий пример демонстрирует теоретический код аналитики, который пытается отправить данные на сервер, используя синхронный XMLHttpRequest в обработчике события unload. Это приводит к задержке выгрузки страницы.
window.addEventListener("unload", logData, false);
function logData() {
const client = new XMLHttpRequest();
client.open("POST", "/log", false); // third parameter indicates sync xhr. :(
client.setRequestHeader("Content-Type", "text/plain;charset=UTF-8");
client.send(analyticsData);
}
Используя метод sendBeacon(), данные будут асинхронно переданы на веб-сервер, когда у агента пользователя будет возможность это сделать, не задерживая выгрузку и не влияя на производительность следующей навигации.
Следующий пример демонстрирует теоретический шаблон кода аналитики, который отправляет данные на сервер с помощью метода sendBeacon().
window.addEventListener("unload", logData, false);
function logData() {
navigator.sendBeacon("/log", analyticsData);
}
См. также
© 2005–2024 MDN contributors.
Licensed under the Creative Commons Attribution-ShareAlike License v2.5 or later.
https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest_API/Synchronous_and_Asynchronous_Requests