欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JS XMLHttpRequest原理與使用方法深入詳解

 更新時間:2020年04月30日 11:02:08   作者:ruoyiqing  
這篇文章主要介紹了JS XMLHttpRequest原理與使用方法,結(jié)合實例形式深入分析了JS XMLHttpRequest相關(guān)原理、使用方法及操作注意事項,需要的朋友可以參考下

本文實例講述了JS XMLHttpRequest原理與使用方法。分享給大家供大家參考,具體如下:

你真的會使用XMLHttpRequest嗎?

看到標(biāo)題時,有些同學(xué)可能會想:“我已經(jīng)用xhr成功地發(fā)過很多個Ajax請求了,對它的基本操作已經(jīng)算挺熟練了?!?我之前的想法和你們一樣,直到最近我使用xhr時踩了不少坑兒,我才突然發(fā)現(xiàn)其實自己并不夠了解xhr,我知道的只是最最基本的使用。
于是我決定好好地研究一番xhr的真面目,可拜讀了不少博客后都不甚滿意,于是我決定認(rèn)真閱讀一遍W3C的XMLHttpRequest標(biāo)準(zhǔn)??赐陿?biāo)準(zhǔn)后我如同醍醐灌頂一般,感覺到了從未有過的清澈。這篇文章就是參考W3C的XMLHttpRequest標(biāo)準(zhǔn)和結(jié)合一些實踐驗證總結(jié)而來的。

Ajax和XMLHttpRequest

我們通常將Ajax等同于XMLHttpRequest,但細(xì)究起來它們兩個是屬于不同維度的2個概念。

以下是我認(rèn)為對Ajax較為準(zhǔn)確的解釋:(摘自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是一種技術(shù)方案,但并不是一種新技術(shù)。它依賴的是現(xiàn)有的CSS/HTML/Javascript,而其中最核心的依賴是瀏覽器提供的XMLHttpRequest對象,是這個對象使得瀏覽器可以發(fā)出HTTP請求與接收HTTP響應(yīng)。

所以我用一句話來總結(jié)兩者的關(guān)系:我們使用XMLHttpRequest對象來發(fā)送一個Ajax請求。

XMLHttpRequest的發(fā)展歷程

XMLHttpRequest一開始只是微軟瀏覽器提供的一個接口,后來各大瀏覽器紛紛效仿也提供了這個接口,再后來W3C對它進(jìn)行了標(biāo)準(zhǔn)化,提出了XMLHttpRequest標(biāo)準(zhǔn)XMLHttpRequest標(biāo)準(zhǔn)又分為Level 1Level 2。
XMLHttpRequest Level 1主要存在以下缺點:

  • 受同源策略的限制,不能發(fā)送跨域請求;

  • 不能發(fā)送二進(jìn)制文件(如圖片、視頻、音頻等),只能發(fā)送純文本數(shù)據(jù);

  • 在發(fā)送和獲取數(shù)據(jù)的過程中,無法實時獲取進(jìn)度信息,只能判斷是否完成;

那么Level 2Level 1 進(jìn)行了改進(jìn),XMLHttpRequest Level 2中新增了以下功能:

  • 可以發(fā)送跨域請求,在服務(wù)端允許的情況下;

  • 支持發(fā)送和接收二進(jìn)制數(shù)據(jù);

  • 新增formData對象,支持發(fā)送表單數(shù)據(jù);

  • 發(fā)送和獲取數(shù)據(jù)時,可以獲取進(jìn)度信息;

  • 可以設(shè)置請求的超時時間;

當(dāng)然更詳細(xì)的對比介紹,可以參考阮老師的這篇文章,文章中對新增的功能都有具體代碼示例。

XMLHttpRequest兼容性

關(guān)于xhr的瀏覽器兼容性,大家可以直接查看“Can I use”這個網(wǎng)站提供的結(jié)果XMLHttpRequest兼容性,下面提供一個截圖。

從圖中可以看到:

  • IE8/IE9、Opera Mini 完全不支持xhr對象

  • IE10/IE11部分支持,不支持 xhr.responseTypejson

  • 部分瀏覽器不支持設(shè)置請求超時,即無法使用xhr.timeout

  • 部分瀏覽器不支持xhr.responseTypeblob

細(xì)說XMLHttpRequest如何使用

先來看一段使用XMLHttpRequest發(fā)送Ajax請求的簡單示例代碼。

function sendAjax() {
 //構(gòu)造表單數(shù)據(jù)
 var formData = new FormData();
 formData.append('username', 'johndoe');
 formData.append('id', 123456);
 //創(chuàng)建xhr對象 
 var xhr = new XMLHttpRequest();
 //設(shè)置xhr請求的超時時間
 xhr.timeout = 3000;
 //設(shè)置響應(yīng)返回的數(shù)據(jù)格式
 xhr.responseType = "text";
 //創(chuàng)建一個 post 請求,采用異步
 xhr.open('POST', '/server', true);
 //注冊相關(guān)事件回調(diào)處理函數(shù)
 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) { ... };
 
 //發(fā)送數(shù)據(jù)
 xhr.send(formData);
}

上面是一個使用xhr發(fā)送表單數(shù)據(jù)的示例,整個流程可以參考注釋。


接下來我將站在使用者的角度,以問題的形式介紹xhr的基本使用。
我對每一個問題涉及到的知識點都會進(jìn)行比較細(xì)致地介紹,有些知識點可能是你平時忽略關(guān)注的。

如何設(shè)置request header

在發(fā)送Ajax請求(實質(zhì)是一個HTTP請求)時,我們可能需要設(shè)置一些請求頭部信息,比如content-type、connection、cookie、accept-xxx等。xhr提供了setRequestHeader來允許我們修改請求 header。

void setRequestHeader(DOMString header, DOMString value);

注意點

  • 方法的第一個參數(shù) header 大小寫不敏感,即可以寫成content-type,也可以寫成Content-Type,甚至寫成content-Type;

  • Content-Type的默認(rèn)值與具體發(fā)送的數(shù)據(jù)類型有關(guān),請參考本文【可以發(fā)送什么類型的數(shù)據(jù)】一節(jié);

  • setRequestHeader必須在open()方法之后,send()方法之前調(diào)用,否則會拋錯;

  • setRequestHeader可以調(diào)用多次,最終的值不會采用覆蓋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();

如何獲取response header

xhr提供了2個用來獲取響應(yīng)頭部的方法:getAllResponseHeadersgetResponseHeader。前者是獲取 response 中的所有header 字段,后者只是獲取某個指定 header 字段的值。另外,getResponseHeader(header)header參數(shù)不區(qū)分大小寫。

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

這2個方法看起來簡單,但卻處處是坑兒。

你是否遇到過下面的坑兒?——反正我是遇到了。。。

  1. 使用getAllResponseHeaders()看到的所有response header與實際在控制臺 Network 中看到的 response header 不一樣

  2. 使用getResponseHeader()獲取某個 header 的值時,瀏覽器拋錯Refused to get unsafe header "XXX"

經(jīng)過一番尋找最終在 Stack Overflow找到了答案。

"simple response header"包括的 header 字段有:Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma;
"Access-Control-Expose-Headers":首先得注意是"Access-Control-Expose-Headers"進(jìn)行跨域請求時響應(yīng)頭部中的一個字段,對于同域請求,響應(yīng)頭部是沒有這個字段的。這個字段中列舉的 header 字段就是服務(wù)器允許暴露給客戶端訪問的字段。

所以getAllResponseHeaders()只能拿到限制以外(即被視為safe)的header字段,而不是全部字段;而調(diào)用getResponseHeader(header)方法時,header參數(shù)必須是限制以外的header字段,否則調(diào)用就會報Refused to get unsafe header的錯誤。

如何指定xhr.response的數(shù)據(jù)類型

有些時候我們希望xhr.response返回的就是我們想要的數(shù)據(jù)類型。比如:響應(yīng)返回的數(shù)據(jù)是純JSON字符串,但我們期望最終通過xhr.response拿到的直接就是一個 js 對象,我們該怎么實現(xiàn)呢?
有2種方法可以實現(xiàn),一個是level 1就提供的overrideMimeType()方法,另一個是level 2才提供的xhr.responseType屬性。

xhr.overrideMimeType()

overrideMimeTypexhr level 1就有的方法,所以瀏覽器兼容性良好。這個方法的作用就是用來重寫responsecontent-type,這樣做有什么意義呢?比如:server 端給客戶端返回了一份document或者是 xml文檔,我們希望最終通過xhr.response拿到的就是一個DOM對象,那么就可以用xhr.overrideMimeType('text/xml; charset = utf-8')來實現(xiàn)。

再舉一個使用場景,我們都知道xhr level 1不支持直接傳輸blob二進(jìn)制數(shù)據(jù),那如果真要傳輸 blob 該怎么辦呢?當(dāng)時就是利用overrideMimeType方法來解決這個問題的。

下面是一個獲取圖片文件的代碼示例:

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

// 這行是關(guān)鍵!
//將響應(yīng)數(shù)據(jù)按照純文本格式來解析,字符集替換為用戶自己定義的字符集
xhr.overrideMimeType('text/plain; charset=x-user-defined');

xhr.onreadystatechange = function(e) {
 if (this.readyState == 4 && this.status == 200) {
 //通過 responseText 來獲取圖片文件對應(yīng)的二進(jìn)制字符串
 var binStr = this.responseText;
 //然后自己再想方法將逐個字節(jié)還原為二進(jìn)制數(shù)據(jù)
 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請求的是一張圖片,通過將 responsecontent-type 改為'text/plain; charset=x-user-defined',使得 xhr 以純文本格式來解析接收到的blob 數(shù)據(jù),最終用戶通過this.responseText拿到的就是圖片文件對應(yīng)的二進(jìn)制字符串,最后再將其轉(zhuǎn)換為 blob 數(shù)據(jù)。

xhr.responseType

responseTypexhr level 2新增的屬性,用來指定xhr.response的數(shù)據(jù)類型,目前還存在些兼容性問題,可以參考本文的【XMLHttpRequest的兼容性】這一小節(jié)。那么responseType可以設(shè)置為哪些格式呢,我簡單做了一個表,如下:

xhr.response 數(shù)據(jù)類型 說明
"" String字符串 默認(rèn)值(在不設(shè)置responseType時)
"text" String字符串
"document" Document對象 希望返回 XML 格式數(shù)據(jù)時使用
"json" javascript 對象 存在兼容性問題,IE10/IE11不支持
"blob" Blob對象
"arrayBuffer" ArrayBuffer對象

下面是同樣是獲取一張圖片的代碼示例,相比xhr.overrideMimeType,用xhr.response來實現(xiàn)簡單得多。

var xhr = new XMLHttpRequest();
xhr.open('GET', '/path/to/image.png', true);
//可以將`xhr.responseType`設(shè)置為`"blob"`也可以設(shè)置為`" arrayBuffer"`
//xhr.responseType = 'arrayBuffer';
xhr.responseType = 'blob';

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

xhr.send();

小結(jié)

雖然在xhr level 2中,2者是共同存在的。但其實不難發(fā)現(xiàn),xhr.responseType就是用來取代xhr.overrideMimeType()的,xhr.responseType功能強大的多,xhr.overrideMimeType()能做到的xhr.responseType都能做到。所以我們現(xiàn)在完全可以摒棄使用xhr.overrideMimeType()了。

如何獲取response數(shù)據(jù)

xhr提供了3個屬性來獲取請求返回的數(shù)據(jù),分別是:xhr.responsexhr.responseText、xhr.responseXML

  • xhr.response

    • 默認(rèn)值:空字符串""

    • 當(dāng)請求完成時,此屬性才有正確的值

    • 請求未完成時,此屬性的值可能是""或者 null,具體與 xhr.responseType有關(guān):當(dāng)responseType"""text"時,值為"";responseType為其他值時,值為 null

  • xhr.responseText

    • 默認(rèn)值為空字符串""

    • 只有當(dāng) responseType"text"、""時,xhr對象上才有此屬性,此時才能調(diào)用xhr.responseText,否則拋錯

    • 只有當(dāng)請求成功時,才能拿到正確值。以下2種情況下值都為空字符串"":請求未完成、請求失敗

  • xhr.responseXML

    • 默認(rèn)值為 null

    • 只有當(dāng) responseType"text"、""、"document"時,xhr對象上才有此屬性,此時才能調(diào)用xhr.responseXML,否則拋錯

    • 只有當(dāng)請求成功且返回數(shù)據(jù)被正確解析時,才能拿到正確值。以下3種情況下值都為null:請求未完成、請求失敗、請求成功但返回數(shù)據(jù)無法被正確解析時

如何追蹤ajax請求的當(dāng)前狀態(tài)

在發(fā)一個ajax請求后,如果想追蹤請求當(dāng)前處于哪種狀態(tài),該怎么做呢?

xhr.readyState這個屬性即可追蹤到。這個屬性是只讀屬性,總共有5種可能值,分別對應(yīng)xhr不同的不同階段。每次xhr.readyState的值發(fā)生變化時,都會觸發(fā)xhr.onreadystatechange事件,我們可以在這個事件中進(jìn)行相關(guān)狀態(tài)判斷。

 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;
 }
狀態(tài) 描述
0 UNSENT (初始狀態(tài),未打開) 此時xhr對象被成功構(gòu)造,open()方法還未被調(diào)用
1 OPENED (已打開,未發(fā)送) open()方法已被成功調(diào)用,send()方法還未被調(diào)用。注意:只有xhr處于OPENED狀態(tài),才能調(diào)用xhr.setRequestHeader()xhr.send(),否則會報錯
2 HEADERS_RECEIVED (已獲取響應(yīng)頭) send()方法已經(jīng)被調(diào)用, 響應(yīng)頭和響應(yīng)狀態(tài)已經(jīng)返回
3 LOADING (正在下載響應(yīng)體) 響應(yīng)體(response entity body)正在下載中,此狀態(tài)下通過xhr.response可能已經(jīng)有了響應(yīng)數(shù)據(jù)
4 DONE (整個數(shù)據(jù)傳輸過程結(jié)束) 整個數(shù)據(jù)傳輸過程結(jié)束,不管本次請求是成功還是失敗

如何設(shè)置請求的超時時間

如果請求過了很久還沒有成功,為了不會白白占用的網(wǎng)絡(luò)資源,我們一般會主動終止請求。XMLHttpRequest提供了timeout屬性來允許設(shè)置請求的超時時間。

xhr.timeout

單位:milliseconds 毫秒
默認(rèn)值:0,即不設(shè)置超時

很多同學(xué)都知道:從請求開始 算起,若超過 timeout 時間請求還沒有結(jié)束(包括成功/失敗),則會觸發(fā)ontimeout事件,主動結(jié)束該請求。

【那么到底什么時候才算是請求開始 ?】
——xhr.onloadstart事件觸發(fā)的時候,也就是你調(diào)用xhr.send()方法的時候。
因為xhr.open()只是創(chuàng)建了一個連接,但并沒有真正開始數(shù)據(jù)的傳輸,而xhr.send()才是真正開始了數(shù)據(jù)的傳輸過程。只有調(diào)用了xhr.send(),才會觸發(fā)xhr.onloadstart 。

【那么什么時候才算是請求結(jié)束 ?】
—— xhr.loadend事件觸發(fā)的時候。

另外,還有2個需要注意的坑兒:

  1. 可以在 send()之后再設(shè)置此xhr.timeout,但計時起始點仍為調(diào)用xhr.send()方法的時刻。

  2. 當(dāng)xhr為一個sync同步請求時,xhr.timeout必須置為0,否則會拋錯。原因可以參考本文的【如何發(fā)一個同步請求】一節(jié)。

如何發(fā)一個同步請求

xhr默認(rèn)發(fā)的是異步請求,但也支持發(fā)同步請求(當(dāng)然實際開發(fā)中應(yīng)該盡量避免使用)。到底是異步還是同步請求,由xhr.open()傳入的async參數(shù)決定。

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

  • method: 請求的方式,如GET/POST/HEADER等,這個參數(shù)不區(qū)分大小寫

  • url: 請求的地址,可以是相對地址如example.php,這個相對是相對于當(dāng)前網(wǎng)頁的url路徑;也可以是絕對地址如http://www.example.com/example.php

  • async: 默認(rèn)值為true,即為異步請求,若async=false,則為同步請求

在我認(rèn)真研讀W3C 的 xhr 標(biāo)準(zhǔn)前,我總以為同步請求和異步請求只是阻塞和非阻塞的區(qū)別,其他什么事件觸發(fā)、參數(shù)設(shè)置應(yīng)該是一樣的,事實證明我錯了。

W3C 的 xhr標(biāo)準(zhǔn)中關(guān)于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.

從上面一段說明可以知道,當(dāng)xhr為同步請求時,有如下限制:

  • xhr.timeout必須為0

  • xhr.withCredentials必須為 false

  • xhr.responseType必須為""(注意置為"text"也不允許)

若上面任何一個限制不滿足,都會拋錯,而對于異步請求,則沒有這些參數(shù)設(shè)置上的限制。

之前說過頁面中應(yīng)該盡量避免使用sync同步請求,為什么呢?
因為我們無法設(shè)置請求超時時間(xhr.timeout0,即不限時)。在不限制超時的情況下,有可能同步請求一直處于pending狀態(tài),服務(wù)端遲遲不返回響應(yīng),這樣整個頁面就會一直阻塞,無法響應(yīng)用戶的其他交互。

另外,標(biāo)準(zhǔn)中并沒有提及同步請求時事件觸發(fā)的限制,但實際開發(fā)中我確實遇到過部分應(yīng)該觸發(fā)的事件并沒有觸發(fā)的現(xiàn)象。如在 chrome中,當(dāng)xhr為同步請求時,在xhr.readyState2變成3時,并不會觸發(fā) onreadystatechange事件,xhr.upload.onprogressxhr.onprogress事件也不會觸發(fā)。

如何獲取上傳、下載的進(jìn)度

在上傳或者下載比較大的文件時,實時顯示當(dāng)前的上傳、下載進(jìn)度是很普遍的產(chǎn)品需求。
我們可以通過onprogress事件來實時顯示進(jìn)度,默認(rèn)情況下這個事件每50ms觸發(fā)一次。需要注意的是,上傳過程和下載過程觸發(fā)的是不同對象的onprogress事件:

  • 上傳觸發(fā)的是xhr.upload對象的 onprogress事件

  • 下載觸發(fā)的是xhr對象的onprogress事件

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

可以發(fā)送什么類型的數(shù)據(jù)

void send(data);

xhr.send(data)的參數(shù)data可以是以下幾種類型:

  • ArrayBuffer

  • Blob

  • Document

  • DOMString

  • FormData

  • null

如果是 GET/HEAD請求,send()方法一般不傳參或傳 null。不過即使你真?zhèn)魅肓藚?shù),參數(shù)也最終被忽略,xhr.send(data)中的data會被置為 null.

xhr.send(data)中data參數(shù)的數(shù)據(jù)類型會影響請求頭部content-type的默認(rèn)值:

  • 如果dataDocument 類型,同時也是HTML Document類型,則content-type默認(rèn)值為text/html;charset=UTF-8;否則為application/xml;charset=UTF-8;

  • 如果dataDOMString 類型,content-type默認(rèn)值為text/plain;charset=UTF-8;

  • 如果dataFormData 類型,content-type默認(rèn)值為multipart/form-data; boundary=[xxx]

  • 如果data是其他類型,則不會設(shè)置content-type的默認(rèn)值

當(dāng)然這些只是content-type的默認(rèn)值,但如果用xhr.setRequestHeader()手動設(shè)置了中content-type的值,以上默認(rèn)值就會被覆蓋。

另外需要注意的是,若在斷網(wǎng)狀態(tài)下調(diào)用xhr.send(data)方法,則會拋錯:Uncaught NetworkError: Failed to execute 'send' on 'XMLHttpRequest'。一旦程序拋出錯誤,如果不 catch 就無法繼續(xù)執(zhí)行后面的代碼,所以調(diào)用 xhr.send(data)方法時,應(yīng)該用 try-catch捕捉錯誤。

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

xhr.withCredentials與 CORS 什么關(guān)系

我們都知道,在發(fā)同域請求時,瀏覽器會將cookie自動加在request header中。但大家是否遇到過這樣的場景:在發(fā)送跨域請求時,cookie并沒有自動加在request header中。

造成這個問題的原因是:在CORS標(biāo)準(zhǔn)中做了規(guī)定,默認(rèn)情況下,瀏覽器在發(fā)送跨域請求時,不能發(fā)送任何認(rèn)證信息(credentials)如"cookies"和"HTTP authentication schemes"。除非xhr.withCredentialstruexhr對象有一個屬性叫withCredentials,默認(rèn)值為false)。

所以根本原因是cookies也是一種認(rèn)證信息,在跨域請求中,client端必須手動設(shè)置xhr.withCredentials=true,且server端也必須允許request能攜帶認(rèn)證信息(即response header中包含Access-Control-Allow-Credentials:true),這樣瀏覽器才會自動將cookie加在request header中。

另外,要特別注意一點,一旦跨域request能夠攜帶認(rèn)證信息,server端一定不能將Access-Control-Allow-Origin設(shè)置為*,而必須設(shè)置為請求頁面的域名。

xhr相關(guān)事件

事件分類

xhr相關(guān)事件有很多,有時記起來還挺容易混亂。但當(dāng)我了解了具體代碼實現(xiàn)后,就容易理清楚了。下面是XMLHttpRequest的部分實現(xiàn)代碼:

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. 每一個XMLHttpRequest里面都有一個upload屬性,而upload是一個XMLHttpRequestUpload對象

  3. XMLHttpRequestXMLHttpRequestUpload都繼承了同一個XMLHttpRequestEventTarget接口,所以xhrxhr.upload都有第一條列舉的7個事件

  4. onreadystatechangeXMLHttpRequest獨有的事件

所以這么一看就很清晰了:
xhr一共有8個相關(guān)事件:7個XMLHttpRequestEventTarget事件+1個獨有的onreadystatechange事件;而xhr.upload只有7個XMLHttpRequestEventTarget事件。

事件觸發(fā)條件

下面是我自己整理的一張xhr相關(guān)事件觸發(fā)條件表,其中最需要注意的是 onerror 事件的觸發(fā)條件。

事件 觸發(fā)條件
onreadystatechange 每當(dāng)xhr.readyState改變時觸發(fā);但xhr.readyState由非0值變?yōu)?code>0時不觸發(fā)。
onloadstart 調(diào)用xhr.send()方法后立即觸發(fā),若xhr.send()未被調(diào)用則不會觸發(fā)此事件。
onprogress xhr.upload.onprogress在上傳階段(即xhr.send()之后,xhr.readystate=2之前)觸發(fā),每50ms觸發(fā)一次;xhr.onprogress在下載階段(即xhr.readystate=3時)觸發(fā),每50ms觸發(fā)一次。
onload 當(dāng)請求成功完成時觸發(fā),此時xhr.readystate=4
onloadend 當(dāng)請求結(jié)束(包括請求成功和請求失?。r觸發(fā)
onabort 當(dāng)調(diào)用xhr.abort()后觸發(fā)
ontimeout xhr.timeout不等于0,由請求開始即onloadstart開始算起,當(dāng)?shù)竭_(dá)xhr.timeout所設(shè)置時間請求還未結(jié)束即onloadend,則觸發(fā)此事件。
onerror 在請求過程中,若發(fā)生Network error則會觸發(fā)此事件(若發(fā)生Network error時,上傳還沒有結(jié)束,則會先觸發(fā)xhr.upload.onerror,再觸發(fā)xhr.onerror;若發(fā)生Network error時,上傳已經(jīng)結(jié)束,則只會觸發(fā)xhr.onerror)。注意,只有發(fā)生了網(wǎng)絡(luò)層級別的異常才會觸發(fā)此事件,對于應(yīng)用層級別的異常,如響應(yīng)返回的xhr.statusCode4xx時,并不屬于Network error,所以不會觸發(fā)onerror事件,而是會觸發(fā)onload事件。

事件觸發(fā)順序

當(dāng)請求一切正常時,相關(guān)的事件觸發(fā)順序如下:

  1. 觸發(fā)xhr.onreadystatechange(之后每次readyState變化時,都會觸發(fā)一次)

  2. 觸發(fā)xhr.onloadstart
    //上傳階段開始:

  3. 觸發(fā)xhr.upload.onloadstart

  4. 觸發(fā)xhr.upload.onprogress

  5. 觸發(fā)xhr.upload.onload

  6. 觸發(fā)xhr.upload.onloadend
    //上傳結(jié)束,下載階段開始:

  7. 觸發(fā)xhr.onprogress

  8. 觸發(fā)xhr.onload

  9. 觸發(fā)xhr.onloadend

發(fā)生abort/timeout/error異常的處理

在請求的過程中,有可能發(fā)生 abort/timeout/error這3種異常。那么一旦發(fā)生這些異常,xhr后續(xù)會進(jìn)行哪些處理呢?后續(xù)處理如下:

  1. 一旦發(fā)生aborttimeouterror異常,先立即中止當(dāng)前請求

  2. readystate 置為4,并觸發(fā) xhr.onreadystatechange事件

  3. 如果上傳階段還沒有結(jié)束,則依次觸發(fā)以下事件:

    • xhr.upload.onprogress

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

    • xhr.upload.onloadend

  4. 觸發(fā) xhr.onprogress事件

  5. 觸發(fā) xhr.[onabort或ontimeout或onerror]事件

  6. 觸發(fā)xhr.onloadend 事件

在哪個xhr事件中注冊成功回調(diào)?

從上面介紹的事件中,可以知道若xhr請求成功,就會觸發(fā)xhr.onreadystatechangexhr.onload兩個事件。 那么我們到底要將成功回調(diào)注冊在哪個事件中呢?我傾向于 xhr.onload事件,因為xhr.onreadystatechange是每次xhr.readyState變化時都會觸發(fā),而不是xhr.readyState=4時才觸發(fā)。

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

上面的示例代碼是很常見的寫法:先判斷http狀態(tài)碼是否是200,如果是,則認(rèn)為請求是成功的,接著執(zhí)行成功回調(diào)。這樣的判斷是有坑兒的,比如當(dāng)返回的http狀態(tài)碼不是200,而是201時,請求雖然也是成功的,但并沒有執(zhí)行成功回調(diào)邏輯。所以更靠譜的判斷方法應(yīng)該是:當(dāng)http狀態(tài)碼為2xx304時才認(rèn)為成功。

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

結(jié)語

終于寫完了......
看完那一篇長長的W3C的xhr 標(biāo)準(zhǔn),我眼睛都花了......
希望這篇總結(jié)能幫助剛開始接觸XMLHttpRequest的你。

最后給點擴展學(xué)習(xí)資料,如果你:

更多關(guān)于ajax相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《jquery中Ajax用法總結(jié)》、《JavaScript中ajax操作技巧總結(jié)》、《PHP+ajax技巧與應(yīng)用小結(jié)》及《asp.net ajax技巧總結(jié)專題

希望本文所述對大家ajax程序設(shè)計有所幫助。

相關(guān)文章

  • JavaScript實現(xiàn)復(fù)選框全選或全取消操作

    JavaScript實現(xiàn)復(fù)選框全選或全取消操作

    這篇文章主要為大家詳細(xì)介紹了JavaScript實現(xiàn)復(fù)選框全選或全取消操作,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • 利用JS動態(tài)生成隔行換色HTML表格的兩種方法

    利用JS動態(tài)生成隔行換色HTML表格的兩種方法

    這篇文章主要介紹了利用JS動態(tài)生成隔行換色HTML表格的兩種方法,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下
    2018-10-10
  • JS監(jiān)聽組合按鍵思路及實現(xiàn)過程

    JS監(jiān)聽組合按鍵思路及實現(xiàn)過程

    這篇文章主要介紹了JS監(jiān)聽組合按鍵思路及實現(xiàn)過程,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-04-04
  • Uniapp中嵌入H5并在H5中跳轉(zhuǎn)到APP的指定頁面方法詳解

    Uniapp中嵌入H5并在H5中跳轉(zhuǎn)到APP的指定頁面方法詳解

    Uniapp是一款基于Vue.js框架的跨平臺開發(fā)工具,支持在一套代碼中開發(fā)出運行于各大平臺的應(yīng)用程序,這篇文章主要給大家介紹了關(guān)于Uniapp中嵌入H5并在H5中跳轉(zhuǎn)到APP的指定頁面的相關(guān)資料,需要的朋友可以參考下
    2023-09-09
  • 純js代碼制作的網(wǎng)頁時鐘特效【附實例】

    純js代碼制作的網(wǎng)頁時鐘特效【附實例】

    下面小編就為大家?guī)硪黄僯s代碼制作的網(wǎng)頁時鐘特效【附實例】。小編覺得聽錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-03-03
  • Bootstrap table使用方法總結(jié)

    Bootstrap table使用方法總結(jié)

    這篇文章主要總結(jié)介紹了Bootstrap table的使用方法,服務(wù)器分頁、客戶端分頁的轉(zhuǎn)換,table刷新,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-05-05
  • 微信小程序 select 下拉框組件功能

    微信小程序 select 下拉框組件功能

    這篇文章主要介紹了微信小程序 select 下拉框組件功能,本文通過實例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-09-09
  • JS往數(shù)組中添加項性能分析

    JS往數(shù)組中添加項性能分析

    這篇文章主要介紹了JS往數(shù)組中添加項性能分析的相關(guān)資料,需要的朋友可以參考下
    2015-02-02
  • 基于javascript實現(xiàn)日歷功能原理及代碼實例

    基于javascript實現(xiàn)日歷功能原理及代碼實例

    這篇文章主要介紹了基于javascript實現(xiàn)日歷效果原理及代碼實例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-05-05
  • 關(guān)于js對textarea換行符的處理方法淺析

    關(guān)于js對textarea換行符的處理方法淺析

    這篇文章主要給大家介紹了關(guān)于js對textarea換行符的處理方法的相關(guān)資料,文中通過示例代碼介紹地方非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-08-08

最新評論