JS XMLHttpRequest原理与使用方法深入详解(5)

另外需要注意的是,若在断网状态下调用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事件中注册成功回调?

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/b3507e46c0e6a376fc8858bdabceb17a.html