如果請求過了很久還沒有成功,為了不會白白占用的網絡資源,我們一般會主動終止請求。XMLHttpRequest提供了timeout屬性來允許設置請求的超時時間。
xhr.timeout
單位:milliseconds 毫秒
默認值:0,即不設置超時
很多同學都知道:從請求開始算起,若超過 timeout 時間請求還沒有結束(包括成功/失敗),則會觸發(fā)ontimeout事件,主動結束該請求。
【那么到底什么時候才算是請求開始?】
——xhr.onloadstart事件觸發(fā)的時候,也就是你調用xhr.send()方法的時候。
因為xhr.open()只是創(chuàng)建了一個連接,但并沒有真正開始數據的傳輸,而xhr.send()才是真正開始了數據的傳輸過程。只有調用了xhr.send(),才會觸發(fā)xhr.onloadstart 。
【那么什么時候才算是請求結束?】
—— xhr.loadend事件觸發(fā)的時候。
另外,還有2個需要注意的坑兒:
可以在 send()之后再設置此xhr.timeout,但計時起始點仍為調用xhr.send()方法的時刻。
當xhr為一個sync同步請求時,xhr.timeout必須置為0,否則會拋錯。原因可以參考本文的【如何發(fā)一個同步請求】一節(jié)。
如何發(fā)一個同步請求
xhr默認發(fā)的是異步請求,但也支持發(fā)同步請求(當然實際開發(fā)中應該盡量避免使用)。到底是異步還是同步請求,由xhr.open()傳入的async參數決定。
open(method, url [, async = true [, username = null [, password = null]]])
method: 請求的方式,如GET/POST/HEADER等,這個參數不區(qū)分大小寫
url: 請求的地址,可以是相對地址如example.php,這個相對是相對于當前網頁的url路徑;也可以是絕對地址如
async: 默認值為true,即為異步請求,若async=false,則為同步請求
在我認真研讀W3C 的 xhr 標準前,我總以為同步請求和異步請求只是阻塞和非阻塞的區(qū)別,其他什么事件觸發(fā)、參數設置應該是一樣的,事實證明我錯了。
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狀態(tài),服務端遲遲不返回響應,這樣整個頁面就會一直阻塞,無法響應用戶的其他交互。
另外,標準中并沒有提及同步請求時事件觸發(fā)的限制,但實際開發(fā)中我確實遇到過部分應該觸發(fā)的事件并沒有觸發(fā)的現象。如在 chrome中,當xhr為同步請求時,在xhr.readyState由2變成3時,并不會觸發(fā) onreadystatechange事件,xhr.upload.onprogress和 xhr.onprogress事件也不會觸發(fā)。
如何獲取上傳、下載的進度
在上傳或者下載比較大的文件時,實時顯示當前的上傳、下載進度是很普遍的產品需求。
我們可以通過onprogress事件來實時顯示進度,默認情況下這個事件每50ms觸發(fā)一次。需要注意的是,上傳過程和下載過程觸發(fā)的是不同對象的onprogress事件:
上傳觸發(fā)的是xhr.upload對象的 onprogress事件
下載觸發(fā)的是xhr對象的onprogress事件
xhr.onprogress = updateProgress; xhr.upload.onprogress = updateProgress; function updateProgress(event) {if(event.lengthComputable) {varcompletedPercent =event.loaded /event.total; } } 可以發(fā)送什么類型的數據
void send(data);
xhr.send(data)的參數data可以是以下幾種類型:
ArrayBuffer
Blob
Document
DOMString
FormData
如果是 GET/HEAD請求,send()方法一般不傳參或傳 。不過即使你真?zhèn)魅肓藚担瑓狄沧罱K被忽略,xhr.send(data)中的data會被置為 .
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的值,以上默認值就會被覆蓋。
評論