Вы действительно полностью понимаете XMLHttpRequest?

внешний интерфейс JavaScript браузер Ajax

Увидев название, некоторые учащиеся могут подумать: «Я использовалxhrуспешно разместил многоAjaxЯ просил, и я достаточно хорошо разбираюсь в его основных операциях. Я думал так же, как и вы, ребята, до недавнего времени я использовалxhrКогда я наступил на множество ям, я вдруг понял, что знаю недостаточно.xhr, я знаю только самое основное использование.
Поэтому я решил провести небольшое исследованиеxhrНо после прочтения многих блогов я не был удовлетворен, поэтому решил внимательно прочитать блог W3C.XMLHttpRequestстандарт. После прочтения стандарта я почувствовал себя просветленным, и я ощутил ясность, которой у меня никогда не было раньше. Эта статья является ссылкой на W3CXMLHttpRequestОн основан на стандарте и сочетается с некоторой практической проверкой.

Ajaxа такжеXMLHttpRequest

мы, как правилоAjaxЭквивалентноXMLHttpRequest, но при ближайшем рассмотрении оказывается, что это два понятия, принадлежащие к разным измерениям.

Вот что я считаю правильнымAjaxБолее точное объяснение: (взято изwhat is Ajax)
AJAX stands for Asynchronous JavaScript and XML. AJAX is a new technique for creating better, faster, and more interactive web applications with the help of XML, HTML, CSS, and Java Script.

AJAX is based on the following open standards:

  • Browser-based presentation using HTML and Cascading Style Sheets (CSS).

  • Data is stored in XML format and fetched from the server.

  • Behind-the-scenes data fetches using XMLHttpRequest objects in the browser.

  • JavaScript to make everything happen.

Из приведенного выше пояснения можно узнать, что:ajaxявляется техническим решением, но неновая технология. он опирается на существующиеCSS/HTML/Javascript, а основная зависимость предоставляется браузеромXMLHttpRequestобъект, который позволяет браузеру испускатьHTTPзапросить и получитьHTTPотклик.

Итак, я резюмирую отношения между ними в одном предложении: мы используемXMLHttpRequestобъект для отправкиAjaxпросить.

XMLHttpRequestистория развития

XMLHttpRequestСначала это был просто интерфейс, предоставляемый браузером Microsoft. Позже его примеру последовали и основные браузеры, которые также предоставили этот интерфейс. Позднее W3C стандартизировал его и предложилXMLHttpRequestстандартный.XMLHttpRequestСтандарт делится наLevel 1а такжеLevel 2.
XMLHttpRequest Level 1Основные недостатки заключаются в следующем:

  • В соответствии с политикой одного и того же источника отправка междоменных запросов невозможна;

  • Невозможно отправлять двоичные файлы (такие как изображения, видео, аудио и т. д.), только текстовые данные;

  • В процессе отправки и получения данных информация о ходе работы не может быть получена в режиме реального времени, и можно судить только о том, завершен ли он;

ТакLevel 2правильноLevel 1улучшен,XMLHttpRequest Level 2Были добавлены следующие функции:

  • Можно отправлять междоменные запросы, если это позволяет сервер;

  • Поддержка отправки и получения двоичных данных;

  • Добавлен объект formData для поддержки отправки данных формы;

  • При отправке и получении данных можно получить информацию о ходе выполнения;

  • Период ожидания запроса может быть установлен;

Конечно, для более подробного сравнения вы можете обратиться кЭта статья г-на Руана, в статье есть конкретные примеры кода для новых функций.

XMLHttpRequestсовместимость

оxhrсовместимость с браузером, вы можете напрямую просматривать результаты, предоставленные веб-сайтом «Могу ли я использовать»Совместимость XMLHttpRequest, скриншот приведен ниже.

Как видно из рисунка:

  • IE8/IE9, Opera Mini вообще не поддерживаютсяxhrобъект

  • IE10/IE11 частично поддерживается, не поддерживаетсяxhr.responseTypeдляjson

  • Некоторые браузеры не поддерживают настройку таймаута запроса, то есть его нельзя использоватьxhr.timeout

  • Некоторые браузеры не поддерживаютxhr.responseTypeдляblob

разрабатыватьXMLHttpRequestкак использовать

Давайте сначала посмотрим на использованиеXMLHttpRequestОтправитьAjaxПростой пример кода для запроса.

function sendAjax() {
  //构造表单数据
  var formData = new FormData();
  formData.append('username', 'johndoe');
  formData.append('id', 123456);
  //创建xhr对象 
  var xhr = new XMLHttpRequest();
  //设置xhr请求的超时时间
  xhr.timeout = 3000;
  //设置响应返回的数据格式
  xhr.responseType = "text";
  //创建一个 post 请求,采用异步
  xhr.open('POST', '/server', true);
  //注册相关事件回调处理函数
  xhr.onload = function(e) { 
    if(this.status == 200||this.status == 304){
        alert(this.responseText);
    }
  };
  xhr.ontimeout = function(e) { ... };
  xhr.onerror = function(e) { ... };
  xhr.upload.onprogress = function(e) { ... };
  
  //发送数据
  xhr.send(formData);
}

Вышеупомянутое является использованиемxhrВ качестве примера отправки данных формы вы можете обратиться к комментариям для всего процесса.


Далее я буду стоять на точке зрения пользователя и представлю ее в форме вопроса.xhrбазовое использование.
Я подробно расскажу о знаниях, связанных с каждым вопросом, и некоторые знания могут быть вами упущены.

Как установить заголовок запроса

отправкаAjaxпросьба (по существуHTTPrequest), нам может понадобиться установить некоторую информацию заголовка запроса, такую ​​какcontent-type,connection,cookie,accept-xxxЖдать.xhrпри условииsetRequestHeaderчтобы мы могли изменить запрос заголовок.

void setRequestHeader(DOMString header, DOMString value);

будь осторожен:

  • Заголовок первого параметра метода нечувствителен к регистру, то есть его можно записать в видеcontent-type, что также можно записать какContent-Type, или даже пишется какcontent-Type;

  • Content-TypeЗначение по умолчанию связано с конкретным типом отправляемых данных, см. раздел [Какие типы данных можно отправлять] этой статьи;

  • setRequestHeaderДолжен бытьopen()После метода,send()Метод вызывается раньше, иначе будет выброшена ошибка;

  • setRequestHeaderМожет вызываться несколько раз, конечное значение не будет перезаписаноoverrideспособ, но добавлениеappendПуть. Вот пример кода:

var client = new XMLHttpRequest();
client.open('GET', 'demo.cgi');
client.setRequestHeader('X-Test', 'one');
client.setRequestHeader('X-Test', 'two');
// 最终request header中"X-Test"为: one, two
client.send();

Как получить заголовок ответа

xhrДля получения заголовков ответов предусмотрено два метода:getAllResponseHeadersа такжеgetResponseHeader. Первый — получить все поля заголовка в ответе, а второй — получить значение указанного поля заголовка. Кроме того,getResponseHeader(header)изheaderАргументы не чувствительны к регистру.

DOMString getAllResponseHeaders();
DOMString getResponseHeader(DOMString header);

Эти два метода кажутся простыми, но везде есть подводные камни.

Вы сталкивались с ямой внизу? - Во всяком случае, сталкивались. . .

  1. использоватьgetAllResponseHeaders()увидеть всеresponse headerс настоящей консольюNetworkвидел вresponse headerРазные

  2. использоватьgetResponseHeader()получитьheaderзначение, браузер выдает ошибкуRefused to get unsafe header "XXX"

После некоторых поисков, наконецStack Overflow нашел ответ.

"simple response header"Включенные поля заголовка:Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma;
"Access-Control-Expose-Headers": Первое, на что следует обратить внимание, это"Access-Control-Expose-Headers"провестимеждоменный запросЭто поле в заголовке ответа, для запроса того же домена в заголовке ответа нет этого поля. указан в этом поле Поле заголовка — это поле, которое сервер разрешает показывать клиенту.

такgetAllResponseHeaders()получить толькоза пределы(то есть рассматривается какsafe) поля заголовка, а не всех полей; вместо этого вызовgetResponseHeader(header)метод,headerпараметр должен бытьза пределыполе заголовка, иначе вызов сообщитRefused to get unsafe headerошибка.

Как указатьxhr.responseтип данных

Иногда мы желаемxhr.responseВозвращается тип данных, который нам нужен. Например: данные, возвращаемые в ответе, представляют собой простую строку JSON, но мы ожидаем, чтоxhr.responseТо, что мы получаем, это объект js напрямую, как мы его реализуем?
Есть 2 способа добиться этого, один из нихlevel 1при условииoverrideMimeType()метод, другойlevel 2только при условииxhr.responseTypeАтрибуты.

xhr.overrideMimeType()

overrideMimeTypeдаxhr level 1Метод есть, так что совместимость браузера хорошая. Цель этого метода состоит в том, чтобы переопределитьresponseизcontent-type, какой смысл это делать? Например: сервер возвращает копию клиентуdocumentилиxmlдокументации, мы надеемся в конечном итоге пройтиxhr.responseесть одинDOMобъект, то вы можете использоватьxhr.overrideMimeType('text/xml; charset = utf-8')реализовать.

Чтобы дать другой сценарий использования, мы все знаемxhr level 1Прямая передача двоичных данных большого двоичного объекта не поддерживается, так что, если вы действительно хотите передать большой двоичный объект? использовалoverrideMimeTypeметод решения этой проблемы.

Вот пример кода для получения файла изображения:

var xhr = new XMLHttpRequest();
//向 server 端获取一张图片
xhr.open('GET', '/path/to/image.png', true);

// 这行是关键!
//将响应数据按照纯文本格式来解析,字符集替换为用户自己定义的字符集
xhr.overrideMimeType('text/plain; charset=x-user-defined');

xhr.onreadystatechange = function(e) {
  if (this.readyState == 4 && this.status == 200) {
    //通过 responseText 来获取图片文件对应的二进制字符串
    var binStr = this.responseText;
    //然后自己再想方法将逐个字节还原为二进制数据
    for (var i = 0, len = binStr.length; i < len; ++i) {
      var c = binStr.charCodeAt(i);
      //String.fromCharCode(c & 0xff);
      var byte = c & 0xff; 
    }
  }
};

xhr.send();

в примере кодаxhrЗапрос на изображение, добавивresponseизcontent-typeИзмените на «text/plain; charset=x-user-defined», чтобыxhrПроанализируйте полученные данные большого двоичного объекта в текстовом формате, конечный пользовательthis.responseTextGOT - это двоичная строка, соответствующая файлу изображения, и, наконец, преобразует его в данные BLOB.

xhr.responseType

responseTypeдаxhr level 2Новый атрибут, указывающийxhr.responseТип данных, все еще есть некоторые проблемы с совместимостью, вы можете обратиться к [XMLHttpRequestСовместимость] в этом разделе. ТакresponseTypeКакие форматы можно задать?Я просто сделал таблицу следующим образом:

стоимость xhr.responseтип данных иллюстрировать
"" Stringнить Значение по умолчанию (без установкиresponseTypeВремя)
"text" Stringнить
"document" Documentобъект надеюсь вернутьсяXMLиспользуется при форматировании данных
"json" javascriptобъект Проблема совместимости, IE10/IE11 не поддерживает
"blob" Blobобъект
"arrayBuffer" ArrayBufferобъект

Ниже приведен пример кода, который также получает изображение по сравнению сxhr.overrideMimeType,использоватьxhr.responseдобиться гораздо проще.

var xhr = new XMLHttpRequest();
xhr.open('GET', '/path/to/image.png', true);
//可以将`xhr.responseType`设置为`"blob"`也可以设置为`" arrayBuffer"`
//xhr.responseType = 'arrayBuffer';
xhr.responseType = 'blob';

xhr.onload = function(e) {
  if (this.status == 200) {
    var blob = this.response;
    ...
  }
};

xhr.send();

резюме

Хотя вxhr level 2, они сосуществуют. Но найти не сложно,xhr.responseTypeэто заменитьxhr.overrideMimeType()из,xhr.responseTypeнамного мощнее,xhr.overrideMimeType()сможет сделатьxhr.responseTypeможет сделать это. Таким образом, теперь мы можем полностью отказаться от использованияxhr.overrideMimeType().

Как получить данные ответа

xhrxhr.response,xhr.responseText,xhr.responseXML

  • xhr.response

    • По умолчанию: пустая строка""

    • Это свойство имеет правильное значение после завершения запроса.

    • Если запрос не завершен, значение этого свойства может быть""илиnull, конкретно сxhr.responseTypeСвязанный: КогдаresponseTypeдля""или"text", значение"";responseTypeДля других значений значение равноnull

  • xhr.responseText

    • По умолчанию пустая строка""

    • только тогда, когдаresponseTypeдля"text",""час,xhrЭто свойство доступно только для объекта и может быть вызвано только в данный момент.xhr.responseText, иначе выдать ошибку

    • Правильное значение можно получить только при успешном выполнении запроса. В следующих двух случаях значение представляет собой пустую строку."": запрос не выполнен, запрос не выполнен

  • xhr.responseXML

    • По умолчаниюnull

    • только тогда, когдаresponseTypeдля"text","","document"час,xhrЭто свойство доступно только для объекта и может быть вызвано только в данный момент.xhr.responseXML, иначе выдать ошибку

    • Правильное значение может быть получено только тогда, когда запрос выполнен успешно и возвращаемые данные верны. Следующие три случая являются значениямиnull: когда запрос не выполнен, запрос завершается с ошибкой, запрос выполняется успешно, но возвращаемые данные не могут быть правильно проанализированы.

как отслеживатьajaxтекущее состояние заявки

размещение одногоajaxПосле запроса, что, если вы хотите отследить, в каком состоянии сейчас находится запрос?

использоватьxhr.readyStateЭто свойство можно проследить. Это свойство доступно только для чтения с 5 возможными значениями, соответствующимиxhrразные этапы. каждый разxhr.readyStateПри изменении значения срабатываетxhr.onreadystatechangeСобытия, мы можем сделать соответствующие государственные суждения в этом событии.

  xhr.onreadystatechange = function () {
    switch(xhr.readyState){
      case 1://OPENED
        //do something
            break;
      case 2://HEADERS_RECEIVED
        //do something
        break;
      case 3://LOADING
        //do something
        break;
      case 4://DONE
        //do something
        break;
    }
стоимость условие описывать
0 UNSENT(исходное состояние, не открыто) В настоящее времяxhrобъект успешно построен,open()метод не вызывался
1 OPENED(открыто, не отправлено) open()метод был вызван успешно,send()Метод еще не вызывался. Примечание: толькоxhrвOPENEDгосударство, чтобы позвонитьxhr.setRequestHeader()а такжеxhr.send(), иначе будет сообщено об ошибке
2 HEADERS_RECEIVED(полученные заголовки ответов) send()Метод был вызван, заголовки ответа и статус ответа были возвращены
3 LOADING(Скачивание тела ответа) тело ответа (response entity body) скачивается, в этом состоянии проходитxhr.responseвозможно, уже есть данные ответа
4 DONE(Весь процесс передачи данных заканчивается) Весь процесс передачи данных завершается, независимо от того, был ли запрос выполнен успешно или нет.

Как установить время ожидания запроса

Если запрос долгое время не был успешным, чтобы не занимать ресурсы сети попусту, мы вообще будем активно завершать запрос.XMLHttpRequestпри условииtimeoutсвойство, позволяющее установить тайм-аут для запроса.

xhr.timeout

Единица: миллисекунды миллисекунды
По умолчанию:0, т.е. не устанавливать тайм-аут

Многие студенты знают, что:запрос на запусксчитать, если большеtimeoutЕсли запрос времени не завершен (включая успех/неудачу), событие ontimeout будет инициировано для активного завершения запроса.

[Так когда же это будетзапрос на запуск? 】
——xhr.onloadstartКогда событие срабатывает, то есть вы вызываетеxhr.send()время метода.
потому чтоxhr.open()просто создает соединение, но фактически не запускает передачу данных, иxhr.send()Здесь начинается настоящий процесс передачи данных. только звониxhr.send(), вызоветxhr.onloadstart.

[Так когда же это будетконец запроса? 】
——xhr.loadendкогда событие срабатывает.

Кроме того, есть две ямы, которые требуют внимания:

  1. допустимыйsend()установить это позжеxhr.timeout, но начальной точкой синхронизации по-прежнему является вызовxhr.send()момент метода.

  2. когдаxhrдля одногоsyncПри синхронном запросеxhr.timeoutдолжен быть установлен на0, иначе будет выброшена ошибка. По этой причине см. раздел [Как отправить синхронный запрос] этой статьи.

Как отправить синхронный запрос

xhrПо умолчанию отправляются асинхронные запросы, но синхронные запросы также поддерживаются (конечно, этого следует избегать в реальной разработке). Является ли это асинхронным или синхронным запросом, определяетсяxhr.open()входящийasyncрешение по параметрам.

open(method, url [, async = true [, username = null [, password = null]]])

  • method: метод запроса, напримерGET/POST/HEADERд., этот параметр не чувствителен к регистру

  • url: запрошенный адрес, который может быть относительным адресом, напримерexample.php,этоотносительноотносительно текущей страницыurlпуть; также может быть абсолютным адресом, напримерhttp://www.example.com/example.php

  • async: значение по умолчаниюtrue, который является асинхронным запросом, еслиasync=false, это синхронный запрос

До того, как я серьёзно изучил стандарт xhr W3C, я всегда думал, что синхронный запрос и асинхронный запрос — это только разница между блокирующим и неблокирующим, а остальные триггеры событий и настройки параметров должны быть одинаковыми, оказывается, я ошибался.

Стандарт W3C xhr оopen()Метод имеет следующее описание:

Throws an "InvalidAccessError" exception if async is false, the JavaScript global environment is a document environment, and either the timeout attribute is not zero, the withCredentials attribute is true, or the responseType attribute is not the empty string.

Из предыдущего абзаца видно, что когдаxhrДля синхронных запросов применяются следующие ограничения:

  • xhr.timeoutдолжно быть0

  • xhr.withCredentialsдолжно бытьfalse

  • xhr.responseTypeдолжно быть""(Обратите внимание"text"тоже нельзя)

Если какое-либо из вышеперечисленных ограничений не выполняется, будет выдано сообщение об ошибке, а для асинхронных запросов ограничений на настройку этих параметров нет.

Я уже говорил, что страница должна стараться избегать использованияsyncСинхронный запрос, почему?
Потому что мы не можем установить время ожидания запроса (xhr.timeoutдля0, то есть неограниченное время). Без ограничения тайм-аута возможно, что синхронный запрос был вpendingсостояние, сервер не возвращает ответ, поэтому вся страница будет постоянно заблокирована, не в состоянии реагировать на другие действия пользователя.

Кроме того, в стандарте не упоминается ограничение запуска событий во время синхронных запросов, но в реальной разработке я столкнулся с некоторыми событиями, которые должны запускаться, но не запускаться. Как в хроме, когдаxhrдля синхронного запроса, вxhr.readyStateЗависит от2стали3, не заводитсяonreadystatechangeмероприятие,xhr.upload.onprogressа такжеxhr.onprogressСобытие также не срабатывает.

Как получить ход загрузки и загрузки

При загрузке или загрузке относительно больших файлов общим требованием к продукту является отображение текущей загрузки и загрузки в режиме реального времени.
мы можем пройтиonprogressСобытие для отображения прогресса в реальном времени, по умолчанию это событие срабатывает каждые 50 мс. Следует отметить, что процесс загрузки и процесс загрузки запускают разные объекты.onprogressмероприятие:

  • Загрузка инициируетсяxhr.uploadобъектonprogressмероприятие

  • Загрузка инициируетсяxhrобъектonprogressмероприятие

xhr.onprogress = updateProgress;
xhr.upload.onprogress = updateProgress;
function updateProgress(event) {
    if (event.lengthComputable) {
      var completedPercent = event.loaded / event.total;
    }
 }

какой тип данных может быть отправлен

void send(data);

xhr.send(data)Данные параметров могут быть следующих типов:

  • ArrayBuffer

  • Blob

  • Document

  • DOMString

  • FormData

  • null

Если это запрос GET/HEAD,send()Методы обычно не передают параметры или передаютnull. Но даже если вы передаете параметры, они в конечном итоге игнорируются,xhr.send(data)Данные будут установлены наnull.

xhr.send(data)Тип данных параметра данных повлияет на заголовок запроса.content-typeЗначение по умолчанию:

  • еслиdataдаDocumentтипа, такжеHTML Documentвведите, затемcontent-typeПо умолчаниюtext/html;charset=UTF-8; иначеapplication/xml;charset=UTF-8;

  • еслиdataдаDOMStringТипы,content-typeПо умолчаниюtext/plain;charset=UTF-8;

  • еслиdataдаFormDataТипы,content-typeПо умолчаниюmultipart/form-data; boundary=[xxx]

  • еслиdataэто другой тип, он не будет установленcontent-typeзначение по умолчанию

Конечно это простоcontent-typeзначение по умолчанию, но если вы используетеxhr.setRequestHeader()установить вручнуюcontent-typeзначение, указанное выше значение по умолчанию будет переопределено.

Кроме того, следует отметить, что если вызов осуществляется в отключенном состоянииxhr.send(data)метод, он выдаст ошибку:Uncaught NetworkError: Failed to execute 'send' on 'XMLHttpRequest'. Как только программа выдает ошибку, она не может продолжать выполнять следующий код без перехвата, поэтому вызовитеxhr.send(data)метод, вы должны использоватьtry-catchОтловить ошибки.

try{
    xhr.send(data)
  }catch(e) {
    //doSomething...
  };

xhr.withCredentialsа такжеCORSкакие отношения

Все мы знаем, что при отправке одного и того же доменного запроса браузерcookieавтоматически добавляется вrequest headerсередина. А вы когда-нибудь сталкивались с таким сценарием: при отправке междоменного запроса,cookieне добавляется автоматически вrequest headerсередина.

Причина этой проблемы:CORSВ стандарте оговорено, что по умолчанию браузеры не могут отправлять никакую аутентификационную информацию при отправке междоменных запросов (credentials)Такие как"cookies"а также"HTTP authentication schemes".пока неxhr.withCredentialsдляtrue(xhrОбъект имеет свойство, называемоеwithCredentials, значение по умолчаниюfalse).

Итак, первопричиной являетсяcookiesЭто также своего рода аутентификационная информация.clientнеобходимо установить вручнуюxhr.withCredentials=true,а такжеserverконец также должен позволятьrequestМожет нести аутентификационную информацию (т.response headerсодержитAccess-Control-Allow-Credentials:true), чтобы браузер автоматическиcookieДобавить вrequest headerсередина.

Кроме того, обратите особое внимание на один момент, когда-то междоменныйrequestможет нести аутентификационную информацию,serverКонец не должен бытьAccess-Control-Allow-OriginУстановить как*, но должно быть установлено на доменное имя запрашивающей страницы.

xhrСвязанные события

классификация событий

xhrСобытий так много, что иногда трудно вспомнить. Но когда я понимаю конкретную реализацию кода, это легко понять. НижеXMLHttpRequestЧасть кода реализации:

interface XMLHttpRequestEventTarget : EventTarget {
  // event handlers
  attribute EventHandler onloadstart;
  attribute EventHandler onprogress;
  attribute EventHandler onabort;
  attribute EventHandler onerror;
  attribute EventHandler onload;
  attribute EventHandler ontimeout;
  attribute EventHandler onloadend;
};

interface XMLHttpRequestUpload : XMLHttpRequestEventTarget {

};

interface XMLHttpRequest : XMLHttpRequestEventTarget {
  // event handler
  attribute EventHandler onreadystatechange;
  readonly attribute XMLHttpRequestUpload upload;
};

Из кода мы видим, что:

  1. XMLHttpRequestEventTargetИнтерфейс определяет 7 событий:

    • onloadstart

    • onprogress

    • onabort

    • ontimeout

    • onerror

    • onload

    • onloadend

  2. КаждыйXMLHttpRequestEстьuploadсвойства, при этомuploadЯвляетсяXMLHttpRequestUploadобъект

  3. XMLHttpRequestа такжеXMLHttpRequestUploadунаследовал то же самоеXMLHttpRequestEventTargetинтерфейс, такxhrа такжеxhr.uploadВ первой статье перечислены 7 событий.

  4. onreadystatechangeдаXMLHttpRequestуникальное событие

Итак, с первого взгляда все ясно:
xhrВсего 8 связанных событий: 7XMLHttpRequestEventTargetСобытие +1 уникальноеonreadystatechangeсобытие; иxhr.uploadтолько 7XMLHttpRequestEventTargetмероприятие.

триггер события

Ниже тот, который я сделал самxhrТаблица условий срабатывания связанных событий, наиболее примечательной из которых являетсяonerrorУсловие запуска события.

мероприятие Условия срабатывания
onreadystatechange в любое времяxhr.readyStateСрабатывает при изменении; ноxhr.readyStateФей0значение становится0не срабатывает.
onloadstart передачаxhr.send()Запускается сразу после метода, еслиxhr.send()Если не вызывается, это событие не будет запущено.
onprogress xhr.upload.onprogressНа этапе загрузки (т.xhr.send()Позже,xhr.readystate=2до) триггер, триггер каждые 50 мс;xhr.onprogressНа этапе загрузки (т.xhr.readystate=3time) срабатывает раз в 50 мс.
onload Запускается при успешном выполнении запроса, в это времяxhr.readystate=4
onloadend Запускается, когда запрос завершается (включая успех запроса и отказ запроса)
onabort при звонкеxhr.abort()пост-триггер
ontimeout xhr.timeoutНе равно 0, начиная с запросаonloadstartначать считать, когда прибудетxhr.timeoutЗапрос установленного времени еще не завершенonloadend, это событие срабатывает.
onerror Во время запроса, еслиNetwork errorвызовет это событие (еслиNetwork error, загрузка еще не завершена, она сработает первойxhr.upload.onerror, запустить сноваxhr.onerror; если это произойдетNetwork error, загрузка завершена, она сработает толькоxhr.onerror).Уведомление, это событие будет инициировано только при возникновении исключения на уровне сети. Для исключений на уровне приложения, таких как возвращенный ответxhr.statusCodeда4xx, не принадлежитNetwork error, так что не сработаетonerrorсобытие, но вызоветonloadмероприятие.

Последовательность запуска события

Когда с запросом все в порядке, соответствующая последовательность срабатывания события выглядит следующим образом:

  1. вызыватьxhr.onreadystatechange(каждый раз послеreadyStateКогда он изменится, он сработает один раз)

  2. вызыватьxhr.onloadstart
    //Начало фазы загрузки:

  3. вызыватьxhr.upload.onloadstart

  4. вызыватьxhr.upload.onprogress

  5. вызыватьxhr.upload.onload

  6. вызыватьxhr.upload.onloadend
    //Загрузка завершена, и начинается фаза загрузки:

  7. вызыватьxhr.onprogress

  8. вызыватьxhr.onload

  9. вызыватьxhr.onloadend

происходитьabort/timeout/errorОбработка исключений

В процессе запроса может произойтиabort/timeout/errorэти 3 исключения. Затем, когда эти исключения происходят,xhrЧто будет сделано дальше? Дальнейшая обработка выглядит следующим образом:

  1. как только это произойдетabortилиtimeoutилиerrorИсключение, немедленно прервать текущий запрос

  2. Будуreadystateустановлен в4, и триггерыxhr.onreadystatechangeмероприятие

  3. Если фаза загрузки не завершена, последовательно запускаются следующие события:

    • xhr.upload.onprogress

    • xhr.upload.[onabort或ontimeout或onerror]

    • xhr.upload.onloadend

  4. вызыватьxhr.onprogressмероприятие

  5. вызыватьxhr.[onabort或ontimeout或onerror]мероприятие

  6. вызыватьxhr.onloadendмероприятие

в которомxhrОбратный вызов успешно зарегистрирован в событии?

Из событий, описанных выше, можно узнать, что еслиxhrЕсли запрос выполнен успешно, он сработаетxhr.onreadystatechangeа такжеxhr.onloadдва события. Итак, в каком событии мы регистрируем обратный вызов успеха? я склоняюсь кxhr.onloadсобытие, потому чтоxhr.onreadystatechangeкаждый разxhr.readyStateбудет срабатывать при изменении вместоxhr.readyState=4срабатывает когда.

xhr.onload = function () {
    //如果请求成功
    if(xhr.status == 200){
      //do successCallback
    }
  }

Приведенный выше пример кода является очень распространенным способом написания: сначала оценитеhttpЯвляется ли код состояния200, если это так, запрос считается успешным, и выполняется обратный вызов успеха. В таких суждениях есть подводные камни, например, при возвращенииhttpкод состояния не200, но201, запрос также выполнен успешно, но логика обратного вызова не выполняется. Следовательно, более надежным методом суждения должен быть: когдаhttpКод состояния2xxили304считается успешным.

  xhr.onload = function () {
    //如果请求成功
    if((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
      //do successCallback
    }
  }

Эпилог

Наконец-то закончил писать...
После прочтения этого длинного стандарта W3C xhr у меня глаза заплыли...
Надеюсь, это краткое изложение поможет тем, кто только начинаетXMLHttpRequestты.

Наконец, дайте несколько расширенных учебных материалов, если вы:

  • хочу действительно понятьXMLHttpRequest, самый надежный способ - посмотреть наСтандарт W3C xhr;

  • Хотите научиться использовать кодXMLHttpRequestОтправить различные типы данных, вы можете обратиться кЭта статья на html5rocks

  • хочу иметь приблизительное представлениеXMLHttpRequestОсновное использование , вы можете обратиться кВведение в XMLHttpRequest MDN;

  • хочу знатьXMLHttpRequestпроцесс разработки, вы можете обратиться кСтатья учителя Руана;

  • хочу знатьAjaxДля основного введения вы можете обратиться кAJAX Tutorial;

  • Если вы хотите узнать о междоменных запросах, вы можете обратиться кКорс-стандарт W3C;

  • хочу знатьhttpсоглашение, вы можете обратиться кHTTP Tutorial;