JS XMLHttpRequest原理與使用方法深入詳解
本文例項講述了JS XMLHttpRequest原理與使用方法。分享給大家供大家參考,具體如下:
你真的會使用XMLHttpRequest嗎?
看到標題時,有些同學可能會想:“我已經用
xhr
成功地發過很多個Ajax
請求了,對它的基本操作已經算挺熟練了。” 我之前的想法和你們一樣,直到最近我使用xhr
時踩了不少坑兒,我才突然發現其實自己並不夠了解xhr
,我知道的只是最最基本的使用。
於是我決定好好地研究一番xhr
的真面目,可拜讀了不少部落格後都不甚滿意,於是我決定認真閱讀一遍W3C的XMLHttpRequest
標準。看完標準後我如同醍醐灌頂一般,感覺到了從未有過的清澈。這篇文章就是參考W3C的XMLHttpRequest
標準和結合一些實踐驗證總結而來的。
Ajax和XMLHttpRequest
我們通常將Ajax
等同於XMLHttpRequest
,但細究起來它們兩個是屬於不同維度的2個概念。
以下是我認為對
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
一開始只是微軟瀏覽器提供的一個介面,後來各大瀏覽器紛紛效仿也提供了這個介面,再後來W3C對它進行了標準化,提出了XMLHttpRequest
標準。XMLHttpRequest
標準又分為Level 1
和Level 2
。
XMLHttpRequest Level 1
主要存在以下缺點:
-
受同源策略的限制,不能傳送跨域請求;
-
不能傳送二進位制檔案(如圖片、視訊、音訊等),只能傳送純文字資料;
-
在傳送和獲取資料的過程中,無法實時獲取進度資訊,只能判斷是否完成;
那麼Level 2
對Level 1
進行了改進,XMLHttpRequest Level 2
中新增了以下功能:
-
可以傳送跨域請求,在服務端允許的情況下;
-
支援傳送和接收二進位制資料;
-
新增formData物件,支援傳送表單資料;
-
傳送和獲取資料時,可以獲取進度資訊;
-
可以設定請求的超時時間;
當然更詳細的對比介紹,可以參考阮老師的這篇文章,文章中對新增的功能都有具體程式碼示例。
XMLHttpRequest相容性
關於xhr
的瀏覽器相容性,大家可以直接檢視“Can I use”這個網站提供的結果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
的基本使用。
我對每一個問題涉及到的知識點都會進行比較細緻地介紹,有些知識點可能是你平時忽略關注的。
如何設定request header
在傳送Ajax
請求(實質是一個HTTP請求)時,我們可能需要設定一些請求頭部資訊,比如content-type
、connection
、cookie
、accept-xxx
等。xhr
提供了setRequestHeader
來允許我們修改請求 header。
void setRequestHeader(DOMString header,DOMString value);
注意點:
-
方法的第一個引數 header 大小寫不敏感,即可以寫成
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();
如何獲取response header
xhr
提供了2個用來獲取響應頭部的方法:getAllResponseHeaders
和getResponseHeader
。前者是獲取 response 中的所有header 欄位,後者只是獲取某個指定 header 欄位的值。另外,getResponseHeader(header)
的header
引數不區分大小寫。
DOMString getAllResponseHeaders();
DOMString getResponseHeader(DOMString header);
這2個方法看起來簡單,但卻處處是坑兒。
你是否遇到過下面的坑兒?——反正我是遇到了。。。
-
使用
getAllResponseHeaders()
看到的所有response header
與實際在控制檯Network
中看到的response header
不一樣 -
使用
getResponseHeader()
獲取某個header
的值時,瀏覽器拋錯Refused to get unsafe header "XXX"
經過一番尋找最終在 Stack Overflow找到了答案。
-
原因1:W3C的 xhr 標準中做了限制,規定客戶端無法獲取 response 中的
Set-Cookie
、Set-Cookie2
這2個欄位,無論是同域還是跨域請求; -
原因2:W3C 的 cors 標準對於跨域請求也做了限制,規定對於跨域請求,客戶端允許獲取的response header欄位只限於“
simple response header
”和“Access-Control-Expose-Headers
” (兩個名詞的解釋見下方)。
"
simple response header
"包括的 header 欄位有:Cache-Control
,Content-Language
,Content-Type
,Expires
,Last-Modified
,Pragma
;
"Access-Control-Expose-Headers
":首先得注意是"Access-Control-Expose-Headers
"進行跨域請求時響應頭部中的一個欄位,對於同域請求,響應頭部是沒有這個欄位的。這個欄位中列舉的 header 欄位就是伺服器允許暴露給客戶端訪問的欄位。
所以getAllResponseHeaders()
只能拿到限制以外(即被視為safe
)的header欄位,而不是全部欄位;而呼叫getResponseHeader(header)
方法時,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
,這樣做有什麼意義呢?比如:server 端給客戶端返回了一份document
或者是 xml
文件,我們希望最終通過xhr.response
拿到的就是一個DOM
物件,那麼就可以用xhr.overrideMimeType('text/xml; charset = utf-8')
來實現。
再舉一個使用場景,我們都知道xhr level 1
不支援直接傳輸blob二進位制資料,那如果真要傳輸 blob 該怎麼辦呢?當時就是利用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
以純文字格式來解析接收到的blob 資料,終端使用者通過this.responseText
拿到的就是圖片檔案對應的二進位制字串,最後再將其轉換為 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',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
中,2者是共同存在的。但其實不難發現,xhr.responseType
就是用來取代xhr.overrideMimeType()
的,xhr.responseType
功能強大的多,xhr.overrideMimeType()
能做到的xhr.responseType
都能做到。所以我們現在完全可以摒棄使用xhr.overrideMimeType()
了。
如何獲取response資料
xhr
提供了3個屬性來獲取請求返回的資料,分別是:xhr.response
、xhr.responseText
、xhr.responseXML
-
xhr.response
-
預設值:空字串
""
-
當請求完成時,此屬性才有正確的值
-
請求未完成時,此屬性的值可能是
""
或者null
,具體與xhr.responseType
有關:當responseType
為""
或"text"
時,值為""
;responseType
為其他值時,值為null
-
-
xhr.responseText
-
預設值為空字串
""
-
只有當
responseType
為"text"
、""
時,xhr
物件上才有此屬性,此時才能呼叫xhr.responseText
,否則拋錯 -
只有當請求成功時,才能拿到正確值。以下2種情況下值都為空字串
""
:請求未完成、請求失敗
-
-
xhr.responseXML
-
預設值為
null
-
只有當
responseType
為"text"
、""
、"document"
時,xhr
物件上才有此屬性,此時才能呼叫xhr.responseXML
,否則拋錯 -
只有當請求成功且返回資料被正確解析時,才能拿到正確值。以下3種情況下值都為
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
單位:milliseconds 毫秒
預設值:0
,即不設定超時
很多同學都知道:從請求開始 算起,若超過 timeout
時間請求還沒有結束(包括成功/失敗),則會觸發ontimeout事件,主動結束該請求。
【那麼到底什麼時候才算是請求開始 ?】
——xhr.onloadstart
事件觸發的時候,也就是你呼叫xhr.send()
方法的時候。
因為xhr.open()
只是建立了一個連線,但並沒有真正開始資料的傳輸,而xhr.send()
才是真正開始了資料的傳輸過程。只有呼叫了xhr.send()
,才會觸發xhr.onloadstart
。
【那麼什麼時候才算是請求結束 ?】
—— xhr.loadend
事件觸發的時候。
另外,還有2個需要注意的坑兒:
-
可以在
send()
之後再設定此xhr.timeout
,但計時起始點仍為呼叫xhr.send()
方法的時刻。 -
當
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
,則為同步請求
在我認真研讀W3C 的 xhr 標準前,我總以為同步請求和非同步請求只是阻塞和非阻塞的區別,其他什麼事件觸發、引數設定應該是一樣的,事實證明我錯了。
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
狀態,服務端遲遲不返回響應,這樣整個頁面就會一直阻塞,無法響應使用者的其他互動。
另外,標準中並沒有提及同步請求時事件觸發的限制,但實際開發中我確實遇到過部分應該觸發的事件並沒有觸發的現象。如在 chrome中,當xhr
為同步請求時,在xhr.readyState
由2
變成3
時,並不會觸發 onreadystatechange
事件,xhr.upload.onprogress
和 xhr.onprogress
事件也不會觸發。
如何獲取上傳、下載的進度
在上傳或者下載比較大的檔案時,實時顯示當前的上傳、下載進度是很普遍的產品需求。
我們可以通過onprogress
事件來實時顯示進度,預設情況下這個事件每50ms觸發一次。需要注意的是,上傳過程和下載過程觸發的是不同物件的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)
的引數data可以是以下幾種型別:
-
ArrayBuffer
-
Blob
-
Document
-
DOMString
-
FormData
-
null
如果是 GET/HEAD請求,send()
方法一般不傳參或傳 null
。不過即使你真傳入了引數,引數也最終被忽略,xhr.send(data)
中的data會被置為 null
.
xhr.send(data)
中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'
。一旦程式丟擲錯誤,如果不 catch 就無法繼續執行後面的程式碼,所以呼叫 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; };
從程式碼中我們可以看出:
-
XMLHttpRequestEventTarget
介面定義了7個事件:-
onloadstart
-
onprogress
-
onabort
-
ontimeout
-
onerror
-
onload
-
onloadend
-
-
每一個
XMLHttpRequest
裡面都有一個upload
屬性,而upload
是一個XMLHttpRequestUpload
物件 -
XMLHttpRequest
和XMLHttpRequestUpload
都繼承了同一個XMLHttpRequestEventTarget
介面,所以xhr
和xhr.upload
都有第一條列舉的7個事件 -
onreadystatechange
是XMLHttpRequest
獨有的事件
所以這麼一看就很清晰了:
xhr
一共有8個相關事件:7個XMLHttpRequestEventTarget
事件+1個獨有的onreadystatechange
事件;而xhr.upload
只有7個XMLHttpRequestEventTarget
事件。
事件觸發條件
下面是我自己整理的一張xhr
相關事件觸發條件表,其中最需要注意的是 onerror
事件的觸發條件。
事件 | 觸發條件 |
---|---|
onreadystatechange |
每當xhr.readyState 改變時觸發;但xhr.readyState 由非0 值變為0 時不觸發。 |
onloadstart |
呼叫xhr.send() 方法後立即觸發,若xhr.send() 未被呼叫則不會觸發此事件。 |
onprogress |
xhr.upload.onprogress 在上傳階段(即xhr.send() 之後,xhr.readystate=2 之前)觸發,每50ms觸發一次;xhr.onprogress 在下載階段(即xhr.readystate=3 時)觸發,每50ms觸發一次。 |
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 事件。 |
事件觸發順序
當請求一切正常時,相關的事件觸發順序如下:
-
觸發
xhr.onreadystatechange
(之後每次readyState
變化時,都會觸發一次) -
觸發
xhr.onloadstart
//上傳階段開始: -
觸發
xhr.upload.onloadstart
-
觸發
xhr.upload.onprogress
-
觸發
xhr.upload.onload
-
觸發
xhr.upload.onloadend
//上傳結束,下載階段開始: -
觸發
xhr.onprogress
-
觸發
xhr.onload
-
觸發
xhr.onloadend
發生abort/timeout/error異常的處理
在請求的過程中,有可能發生 abort
/timeout
/error
這3種異常。那麼一旦發生這些異常,xhr
後續會進行哪些處理呢?後續處理如下:
-
一旦發生
abort
或timeout
或error
異常,先立即中止當前請求 -
將
readystate
置為4
,並觸發xhr.onreadystatechange
事件 -
如果上傳階段還沒有結束,則依次觸發以下事件:
-
xhr.upload.onprogress
-
xhr.upload.[onabort或ontimeout或onerror]
-
xhr.upload.onloadend
-
-
觸發
xhr.onprogress
事件 -
觸發
xhr.[onabort或ontimeout或onerror]
事件 -
觸發
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
的基本使用,可以參考MDN的XMLHttpRequest介紹; -
想了解
XMLHttpRequest
的發展歷程,可以參考阮老師的文章; -
想了解
Ajax
的基本介紹,可以參考AJAX Tutorial; -
想了解跨域請求,則可以參考W3C的 cors 標準;
-
想了解
http
協議,則可以參考HTTP Tutorial;
更多關於ajax相關內容感興趣的讀者可檢視本站專題:《jquery中Ajax用法總結》、《JavaScript中ajax操作技巧總結》、《PHP+ajax技巧與應用小結》及《asp.net ajax技巧總結專題》
希望本文所述對大家ajax程式設計有所幫助。