背景介绍
某次路过同事的工位,刚好看到同事在写面试评价,看到里面有一个问题:组件卸载时自动取消异步请求问题,不及格。
我:???
现在fetch已经支持手动abort请求了吗?
于是上网去查各种资料:how to abort fetch http request when component umounts
然后得到的各种各样的资料里面,看起来比较靠谱的是这样一种:
componentDidMount(){
this.mounted = true;
this.props.fetchData().then((response) => {
if(this.mounted) {
this.setState({ data: response })
}
})
}
componentWillUnmount(){
this.mounted = false;
}
复制代码
我:????
就这样吗?
然而这个写法并没有真的abort
掉fetch
请求,只是不去响应fetch成功之后的结果而已,这完全没有达到取消异步请求的目的。
于是我去问了问同事,如何真正abort
掉一个已经发送出去的fetch请求。
同事跟我说:现在浏览器还不支持abort
掉fetch
请求。
我:……
同事继续:不过我们可以通过Promise.race([cancellation, fetch()])
的方式,在fetch真正结束之前先调用cancellation
方法来返回一个reject
,直接结束这个Promise
,这样就可以看似做到abort
掉一个正在发送的fetch,至于真正的fetch
结果是怎么怎样的我们就不需要管了,因为我们已经得到了一个reject
结果。
我:那么有具体实现方法的wiki吗?
同事:我们代码里面就有,你去看看就行。
我:……(我竟然不知道!)
于是我就连读带问,认真研读了一下组件卸载自动取消异步请求的代码。
实现
整个代码的核心部分确实是刚才同事提到的那一行代码:return Promise.race([cancellation, window.fetch(input, init)]);
不过这里的cancellation
其实是另一个Promise
,这个Promise
负责注册一个abort
事件,当我们组件卸载的时候,主动触发这个abort
事件,这样最后如果组件卸载之前,fetch
请求已经响应完毕,就走正常逻辑,否则就因为我们触发了abort事件返回了一个reject
的响应结果。
const realFetch = window.fetch;
const abortableFetch = (input, init) => {
// Turn an event into a promise, reject it once `abort` is dispatched
const cancellation = new