和浏览器异步请求取消相关的那些事

我们开发web页面时候,也许会遇到和异步请求取消相关的问题。

如:在一个请求发送之后,用户做了一个取消指令,为了节省资源,我们需要把已经被用户取消的请求终止掉;或者是一个页面正在用ajax请求后台,突然页面发生了跳转,而我们未完成的ajax莫名其妙地走进了error里面了。

为了解决这两问题,我们今天一起看看和异步请求取消相关的那些事。

1.ajax的取消

当我们创建一个XMLHttpRequest对象的时候,我们就会发现两个api——abert和onabort,这就是终止异步请求的方法与其响应事件。

执行完abort之后,浏览器和被请求的服务器都会发生什么呢?MDN的解释非常的简单,就是中断已发送的请求。这个请求指的是http请求,而不是tcp连接,这样就会出现一个问题,基于http请求原理,当一个请求从客户端发出去之后,服务器端收到请求后,一个请求过程就结束了,这时就算是客户端abort这个请求,服务器端仍会做出完整的响应,只是这个响应客户端不会接收罢了。

所以这个abort是仅给客户端使用的,不能作为供服务器端判断请求是否继续执行的依据

那么abort的请求否对客户端有哪些影响呢?我们可以做一个实验。

var xhr = new XMLHttpRequest();
xhr.open("GET","#");
xhr.send();
xhr.onload = function(){
    console.log("abort前");
    console.log(xhr.readyState);
    console.log(xhr.status);
    xhr.abort();
    console.log("abort后");
    console.log(xhr.readyState);
    console.log(xhr.status);
}

我们可以看到readyState和status在abort之后被重置回0。

那么我们能用这两个参数作为判断请求被abort的依据吗?首先能够让status等于0的情况太多了,如请求本地资源、网络不可用、请求超时,这些都可以让status被置0;readyState等于0能否作为请求是否被abort了还不好说,需要进一步判断,readyState等于0相当于请求未初始化,请求都已经send了readyState却等于0,笔者认为是可以作为abort的判断依据的,但是无法完全证明。

有没有更可靠的证明请求是否执行了abort方法呢?有,答案是使用onabort,onabort作为abort的响应函数,是最直接有效的判断abort手段。

2.页面跳转时候ajax会自动“abert”

笔者从前认为abort离我很远,但是在实际项目中,笔者发现页面我开发的请求经常被abort。这个abort动作当然不是我发起的,也不是用户发起的,他是浏览器自动发起的。笔者发现一个页面跳转的时候,浏览器会自动把所有响应未完成的请求执行“abort”,而响应已完成的请求则不会这样。我们可以做一个实验

//要在chrome或者webkit内核上运行
var xhr = new XMLHttpRequest();
//访问一个不存在的地址 取保请求不会马上响应
xhr.open("GET","http://aaa.bbbbbbbb.com");
xhr.send();
xhr.onabort = function(){
    console.log(xhr.readyState);
    console.log(xhr.status);
    alert("执行onabort");
};
setTimeout(function(){
    //模拟跳转页面
    location.href = "http://www.baidu.com"
},0);

结果网页上弹出了一个alert,显示着"执行onabort"。

再看控制台,我们会发现status不变还是0,而readyState却是4,这也是浏览器发出的abort和手动执行abort最大不同。

以上测试仅在chrome上有效,ie、edge、火狐在页面跳转的时候,不会触发未完成的请求的onabort事件,但是会触发onreadystatechange事件。不管怎么讲,当页面发生跳转的时候,浏览器可能会“abert”我们的异步请求。

3.jquery对abort的处理

jquery又是如何对abort封装的呢?我们在使用$.ajax(包括众多用$.ajax封装的方法,如$.get、$.post)的时候,会返回一个xhr对象,这个基于$.deferred.promise封装的jquery自己的对象,而不是原始的XMLHttpRequest或者ie的ActiveXObject对象。在这个对象中定义了如abort等方法,使得开发者可以手动abort一个ajax请求。

var xhr = $.ajax(url);
xhr.abort();

另外,jquery的超时也是通过setTimeout和abort实现的,所以当你使用jquery发出的请求超时的时候,实际上是被jquery把请求abort了。如何区分jquery的超时和手动abort呢?方法就是靠stutusText,对于timeout和abort两个客户端做出的响应,jquery会给stutusText设定固定的值,abort的时候,stutusText的值为“abort”,超时的时候stutusText值是“timeout”。

4.jquery与页面跳转的ajax“abert”

如果仅仅是页面跳转的时候,chrome浏览器会自动执行未完成的请求的abort方法,那笔者也不会专门写一个章节去分析这个过程。因为笔者发现jquery的$.ajax这个方法中,未完成的ajax在页面跳转的时候,也会出发error事件,而且你区分不出来是浏览器取消还是请求真的发生了error

大家可以运行如下代码,在ie和chrome两大浏览器下都会弹出的alert对话框。

var xhr = $.ajax({
    type:"get",
    url:"http://aaa.bbbbbbbb.com",
    error:function(){
        console.log(arguments);
        console.log(xhr.readyState);
        console.log(xhr.status);
        alert("执行onerror");
    }
});

setTimeout(function(){
    //模拟跳转页面
    location.href = "http://www.baidu.com"
},100);

页面一跳转就进error,而且status和readyState都是0,stutusText仅仅显示一个“error”,jquery真是让人佩(蛋)服(疼)的五体投地-_-||。

为什么会这样呢?那么jquery又是如何处理onabort的呢?

笔者发现jquery1.x和2.x都会触发这个现象,所以分别参考jquery1.x和2.x的源码讨论。

jquery1.x其实并没有监听onabort事件,而是统一监听onreadystatechange(具体可以参考github的xhr.jsajax.js源码),根据status是否是成功响应的http状态码,来确定执行error还是success方法。同时,jquery也没有获取浏览器的readyState的值,而是通过status是否为0去计算自己的xhr.readyState,可以说所有的响应全是靠status一个变量决定的,这就导致了我们无法区分浏览器取消事件还是真正的错误的问题。

在jquery2.x中,不再仅监听onreadystatechange(具体可以参考github的xhr.jsajax.js源码),而是对onload、onerror、onabort(不支持onabort事件的ie9还是监听的onreadystatechange事件)全面监听,并由这些响应事件的结果去确定究竟执行error还是success。这个处理看似更合理了,然而却并没有什么卵用,因为没有监听chrome浏览器的readyState实际值,仍然是通过status去计算readyState,所以仍然会触发error事件,而且xhr.readyState的值还是0,stutusText还是仅仅显示一个“error”。

说白了这就是jquery的一个bug,仅仅是根据status是否为0去判断ajax结果,同时不返回浏览器真正的readyState值;当然我们也可以说是浏览器的bug,为什么chrome浏览器在页面跳转的时候要abort我们的请求呢。

不管怎么样,笔者建议在使用jquery的$ajax做异步请求的时候,千万不要在error回调中使用系统的模态框(如alert、confirm等),否则用户在使用你的页面的时候经常会出现意想不到的弹框。

5.fetch及promise如何取消与取消处理的

fetch作为ajax的升级版,越来越多的浏览器已经支持他了,那fetch又是如何取消异步请求的呢?答案fetch暂时是不能被取消的...,因为没有对应的api。

虽然不能取消,但是还是有替代品,当然这只是自欺欺人的做法,因为fetch根本没有被真正取消,他的资源也没有被释放。

标题和fetch挂钩,这让笔者感觉有点大,因为笔者现在的项目中还不准备使用fetch。实际上笔者更想聊一聊我对abort和promise的看法,因为不管我们用不用fetch,将异步请求封装成promise供后续处理都是我们现在开发的主流做法,那么如果用promise做abrot呢?

promise仅有两个完成态,resolved和rejected。一个可以当做success处理,另一个可以当做error处理。那我们的abort的结果应该算在哪个里面呢?abort肯定不能将其看成success,但是abort是我们主动的动作(也可能是浏览器发出的被动abort),并不是发生真的发生了错误,将其列入error看起来也不合适。

其实所有非预想的结果都是异常,所以abort当然也是异常,既然是异常就应该当rejected对待。只是resolved的处理方案因为结果是预期中的,所有处理起来比较容易,但是rejected的处理往往很困难,因为各种异常的处理方法是应该不一样的。比如abort这种异常,如果是因为用户主动操作而产生的异常,那这种异常是不应该提示给用户的,所以abort引起的异常应该包装为特定的异常再进行rejected处理,以便在catch中,可以知道是什么异常,并作出对应的处理。

参考:https://github.com/camsong/blog/issues/2

时间: 2024-12-19 08:57:10

和浏览器异步请求取消相关的那些事的相关文章

关于异步请求时出现的问题

第一个问题: 在struts2的配置文件中  如果不指定type的类型 那么在action中任何跳转都是默认forword类型的  称为内部跳转  比如下面的例子 第一个例子: 管理员登录成功之后  跳转到list.jsp页面 但是网页上的url却是下面的 第二个例子: 用户登录成功之后  跳转到detail.jsp页面 但是url 说了这么多   就是为了解决上面的操作和异步请求时   容易出现的问题 继续拿第二个例子来说明  如果在该详情页面中  我使用异步操作(比如做更新) 只要我们使用了

ASIHTTPRequest取消异步请求

今天碰到一个问题 异步请求等待中 cancel后会发生什么,网上找了下资料说取消的请求默认都会按请求失败处理,并调用请求失败delegate 查找到的资料具体解释了下ASIHTTPRequest取消异步请求 首先,同步请求是不能取消的.其次,不管是队列请求,还是简单的异步请求,全部调用[ request cancel ]来取消请求. 取消的请求默认都会按请求失败处理,并调用请求失败delegate.如果不想调用delegate方法,则设置:[ request clearDelegatesAndC

【转】关于“不同浏览器的并发异步请求”的简单研究

之前在开发过程中有发现在一个页面内同时对单个url发出多个Ajax请求最后完成回调的只有最后一个.具体是什么原因导致的呢?针对这个问题,本人做了进一步的测试. 对于单个页面内并发的异步请求分为以下几种情况: 并发多个相同url的请求 并发多个不同url的请求(参数不同,协议相同) 并发多个不同url的请求(参数和协议都不同) 测试方法: 客户端:同时发出N异步请求,并输出发出请求的时间:当服务器返回相应数据后将接受请求的时间输出,并输出之前所发送的请求的标识ID. 服务器:服务器接到请求后延时一

iOS开发--用户点击频繁,多个异步网络请求取消问题?

一.业务环境描述 当一个view同时添加两个tableView为subView的时候,两个tableView分别为mainTable和subTable. 当用户点击mainTable上的某一条数据时,再去发送网络请求subTable中的数据. 存在问题的地方:例如当用户在mainTable上频繁点击各条cell上面的数据时,会出现subTable中数据错乱问题.那么如何取消之前的网络请求呢??? 注:网络请求为异步请求 二.解决方法 1.实例化一个AFHTTPRequestOperationMa

Servlet 3.0笔记之异步请求Comet推送iFrame示范

Servlet3规范提出异步请求,绝对是一巨大历史进步.之前各自应用服务器厂商纷纷推出自己的异步请求实现(或者称comet,或者服务器推送支持,或者长连接),诸如Tomcat6中的NIO连接协议支持,Jetty的continuations编程架构,SUN.IBM.BEA等自不用说,商业版的服务器对Comet的支持,自然走在开源应用服务器前面,各自为王,没有一个统一的编程模型,怎一个乱字了得.相关的comet框架也不少,诸如pushlet.DWR.cometd;最近很热HTML5也不甘寂寞,推出W

SpringMVC环境下实现的Ajax异步请求(JSON格式数据)

一 环境搭建 首先是常规的spring mvc环境搭建,不用多说,需要注意的是,这里需要引入jackson相关jar包,然后在spring配置文件"springmvc-servlet.xml"中添加json解析相关配置,我这里的完整代码如下: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schem

JavaScrpit中异步请求Ajax实现

在前端页面开发的过程中,经常使用到Ajax请求,异步提交表单数据,或者异步刷新页面. 一般来说,使用Jquery中的$.ajax,$.post,$.getJSON,非常方便,但是有的时候,我们只因为需要ajax功能而引入Jquery比较不划算. 所以接下来便用原生JavaScrpit实现一个简单的Ajax请求,并说明ajax请求中的跨域访问问题,以及多个ajax请求的数据同步问题. JavaScript实现Ajax异步请求 简单的ajax请求实现 Ajax请求的原理是创建一个XMLHttpReq

从零开始学 Web 之 Ajax(五)同步异步请求,数据格式

大家好,这里是「 从零开始学 Web 系列教程 」,并在下列地址同步更新...... github:https://github.com/Daotin/Web 微信公众号:Web前端之巅 博客园:http://www.cnblogs.com/lvonve/ CSDN:https://blog.csdn.net/lvonve/ 在这里我会从 Web 前端零基础开始,一步步学习 Web 相关的知识点,期间也会分享一些好玩的项目.现在就让我们一起进入 Web 前端学习的冒险之旅吧! 一.同步请求与异步

使用AJAX技术发送异步请求,HTTP服务端推送

使用AJAX技术发送异步请求 什么是AJAX AJAX指一步Javascript和XML(Asynchronous JavaScript And XML),它是一些列技术的组合,简单来说AJAX基于XMLHttpRequest让我们在不重载页面的情况下和服务器进行数据交换. 加上JavaScript和DOM(Document Object Model,文档对象模型),我们就可以在接收到响应数据后局部更新页面.XML指的是数据的交互模式,可以是纯文本(Plain Text).HTML或JSON.