谣言粉碎机 - 极短时间内发送两个 Odata request,前一个会自动被 cancel 掉?
背景
有时我们能在 Chrome 开发者工具的 Network tab 里观察到 SAP UI5 应用会发出某些状态为"取消"的 OData 请求。如下图第五个请求。
之前有一种似是而非的说法:极短时间内发送两个 OData 请求,则第一个会自动被 cancel 掉。
这个说法从字面上看,有两点值得推敲:
1. cancel 掉,被谁 cancel 掉?UI5 框架还是 Chrome?
2. “极短”,多短算极短?
我用代码在 for 循环里一共发 10 个 OData 请求:
无论是同步还是异步,都没有任何的请求被 cancel。
10 个同步请求:
10 个异步请求:
就算发 100 个 request 都不会有一个 request 被 cancel:
验证结果,之前的说法“极短时间内发送两个 OData request,前一个会自动被 cancel 掉”是错误的。
那再回到本文第一张图观察到的 cancel 的场景, 原因究竟是什么?
观察产生了被取消的 OData 请求的应用代码,观察到第 523 行有这个 refresh 操作:
在这个方法的第 601 行,bChangeDetected 变量为 true 导致 abortPendingRequest 的调用。
abortPendingRequest 的注释已经很清楚地说明问题了。
什么情况下会导致 AbortPendingRequest? 直接使用 Chrome 开发者工具的全文搜索得到答案:OData model 的三个 API: filter, sort, refresh
下面是我的同事 Li Ben 的进一步补充。
关于这个现象发生的原因和条件的问题
1. 在哪里可以看到这个 cancel 现象?
在我们的 live search 功能上,如果输入较快或者正常速度输入,会看到前面很多输入请求都会被 cancel 掉:
如果输入较慢则不会:
真的是快慢的原因吗?
仔细观察 network 发现,真正的原因是当上一次的 network 还处于 pending 状态的时候,继续输入发起的请求就会 cancel 掉上一次的请求:
继续深究, 这是在哪里做到的?
在 SAP UI5 的 OData 框架里面有这样的实现:
在 ODataModel.js 中维护了一个 http request 的 pending list,将已经发送但是还没有收到响应的 request 对象都缓存在这个列表中:
每次发起 OData 请求的时候都会调用 ODataModel 的_request()方法,这个方法会把当前的 request 加到 pending list 中,并且通过一个 wrap method 包装回调函数,确保在响应返回的时候首先把缓存的 request 对象从 pending list 中拿掉:
每次在 OData Model 上发起 filter, sort, refresh 操作的时候,都会检查是否存在 pending 的 request 对象,如果存在未完成的请求,abort 掉它:
回答上面的问题,在什么情况下会发生这种现象?
1. 同一个 ODataModel 的 instance 上发出的连续请求,因为 pending list 是缓存在 this 级别上面的。
2. 前一个 Http 请求的 network 还处于 pending status 的时候。
3. 就读 ODataModel 的代码和观察到的现象,在 ODataModel 上发起 filter, sort 或者 refresh 的时候。
为什么在 OData 的 request 对象上发起 abort 调用就可以取消底层的 network call?
简单的说,UI5 里面的 OData Request 对象是底层的 Ajax Request 对象 XmlHttpRequest 的一个代理,在 ODataModel 的_submit 方法中:
具体实现是 UI5 中利用了一个第三方的库 datajs,datajs 最终会调用浏览器的底层 http 对象 XMLHttpRequest:
要获取更多 Jerry 的原创技术文章,请关注公众号"汪子熙":
版权声明: 本文为 InfoQ 作者【Jerry Wang】的原创文章。
原文链接:【http://xie.infoq.cn/article/f1860263ba650bfd3e9397e09】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论