如何取消 HTTP 请求?是在面试过程中经常遇到的一个问题,并且在日常开发中也会遇到,比如不使用防抖的情况下如何取消请求,再比如一个请求等待时间过长,用户不想等了,可以取消这个请求。
最近在使用 Angular 的过程中,用到了 switchMap 操作符,发现每次发送新的请求时,上一个请求都会被自动取消掉,为了探究原理,就有了本文章。
我们知道,浏览器能发送请求,靠两个重要的 API,一个是比较老旧的 XHR,另一个比较新的 Fetch。所以本文会分别介绍使用 XHR 和 Fetch 如何取消请求,并且会分析一下 Angular 内置的 http 模块是怎样取消请求的。
XHR 取消请求
为了测试,首先创建一个 index.html
文件,内容如下,并且我们会使用 http://httpbin.org/
提供的开放 API 来发送和取消请求。
XHR 使用实例的 abort()
方法来取消请求。
先点击 Begin
,并且在 3 秒内点击 Abort
,效果如下:
可以看到,在点击 Abort 的时候,当前的 HTTP 请求的 Status 会变成 canceled 的状态。
Fetch 取消请求
Fetch 使用 AbortController
来取消请求。
同样先点击 Begin
,并且在 3 秒内点击 Abort
,效果如下:
可以看到,当点击 Abort
之后会调用 controller
实例的 Abort
方法,并且在 singal
上订阅的 abort
event 也会被执行。与 XHR 不同的是,控制台会多打印一条 Error 信息。
Angular 中的 http 模块取消请求
了解了 XHR 和 Fetch 取消请求的方式之后,我们来看一下 Angular 中是使用的哪种方式。
首先在 sandbox 上创建一个 Angular 项目,地址:演示代码
通过多次点击按钮可以发现,每次点击都会先取消上一个请求,并且发送新的请求。那么 Angular 和 RxJS 内部是怎么实现的呢?
带着这个问题,我们首先来看 switchMap 的代码:switchMap
其中有这么几行是我们关心的:
我们知道 switchMap 操作符的行为是每次上游产生数据的时候都会去调用函数参数 project,只要有新的内部 Observable 产生,就会立刻退订之前的内部 Observable 对象,所以 Angular 中取消 http 请求的代码应该是该请求内部 Observable 的返回值,比如对应到演示代码中就是 this.http.get(url)
方法内部 Observable 的返回值。
接下来我们去查看 Angular http 模块的 get
方法,地址:client.ts
通过查看代码,可以发现 get
方法会调用一个名为 handle
的函数,这个函数返回了一个 Observable,而这个 Observable 的返回值也是一个函数,代码如下:
通过 abort
和变量命名也能猜出来,Angular 中内置的 http 模块是使用的 XHR 来发送请求,更详细的过程可以阅读 Angular http 模块的源码。