参考文章1
参考文章2
一、什么是AbortController (abort 意为 中止/废弃)
AbortController是一个控制器对象(DOM API),可通过new
构造函数的方式,生成控制器实例对象,根据需要终止/取消一个或多个Web请求/监听事件
通过new
生成的控制器实例对象很简单,只有一个方法abort()
,和一个对象属性signal(AbortSignal对象)
,signal
属性上还有aborted、onabort、reason
三个属性,主要用到aborted
属性,表示是否已终止,其初始值为false
当控制器的实例方法abort()
被调用时,实例对象的signal
属性会触发abort
事件,并将signal
上的aborted
属性变为true
触发abort
事件可被监听,signal.addEventListener('abort',()=>{},{once:true})
,且与同一个signal
关联的监听器只会触发一次abort
事件,故在添加abort
事件监听器之前,需确保该监听器的abort
事件未触发过,检查signal.aborted
属性是否为初始值false
任何绑定到signal
的事件监听器都应使用{once:true}
,以确保在处理abort
事件后立即删除事件监听器,否则可能导致内存泄漏
二、AbortController控制器怎么用
// 1.创建监听器实例对象
let controller = new AbortController();
// 2.获取signal对象
let signal = controller.signal;
// 3.给signal对象绑定监听事件
signal.addEventListener('abort',() => alert('abort!'),{ once: true });
// 4.在需要中止的任何时刻中止
controller.abort();
三、AbortController和fetch配合使用
AbortController
可用于中止fetch
请求
let controller = new AbortController();
// 将`AbortController`的`signal`对象属性作为fetch的第二个参数options上的可选属性进行传递
fetch(url,{
signal:controller.signal
})
// `fetch`方法会监听属性`signal`上的`abort`事件,在想要中止fetch时调用controller.abort()即可
controller.abort();
当fetch请求被中止时,它的promise就进入reject回调,并返回名为AbortError的DOMException,因此需使用try/catch
进行容错处理
let controller = new AbortController();
// 1s后中止fetch请求
setTimeout(()=>{
controller.abort()
}, 1000);
// 容错处理
try{
let response = fetch(url,{
signal:controller.signal
})
}catch(err){
if(err.name === 'AbortError'){
alert('Aborted!')
}else{
throw err;
}
}
注意:已经abort
的fetch请求是不能重复调用的,调用已经被abort
的fetch请求,当读到signal
的状态是aborted
时,会直接进reject回调
fetch源码:
// fetch源码仓库地址: https://github.com/github/fetch/blob/master/fetch.js
if (request.signal && request.signal.aborted) {
return reject(new DOMException('Aborted', 'AbortError'))
}
AbortController
允许一次取消多个 fetch请求
let urls = [...]; // 要并行 fetch 的 url 列表
let controller = new AbortController();
// 一个 fetch promise 的数组
let fetchJobs = urls.map(url => fetch(url, {
signal: controller.signal
}));
let results = await Promise.all(fetchJobs);
// controller.abort() 被从任何地方调用,
// 它都将中止所有 fetch
如果我们有自己的与 fetch 不同的异步任务,我们可以使用单个 AbortController 中止这些任务以及 fetch。
在我们的任务中,我们只需要监听其 abort 事件:
let urls = [...];
let controller = new AbortController();
let ourJob = new Promise((resolve, reject) => { // 我们的任务
...
controller.signal.addEventListener('abort', reject);
});
let fetchJobs = urls.map(url => fetch(url, { // fetches
signal: controller.signal
}));
// 等待完成我们的任务和所有 fetch
let results = await Promise.all([...fetchJobs, ourJob]);
// controller.abort() 被从任何地方调用,
// 它都将中止所有 fetch 和 ourJob
四、AbortController和addEventListener配合使用
AbortController
可作为addEventListener
的第三个参数options上的可选属性
意思是,将controller的signal作为参数传进去,调用abort()
,该监听器就会被移除
我们知道,取消监听器的方法removeEventListener(type,callback),这个callback必须和开启监听的callback是同一个函数引用,也就是说得把callback存下来,但有了signal就不用了
示例:实现按住鼠标后,监听鼠标的移动,松开鼠标后,取消监听
const controller = new AbortController();
function callback (e) {
document.addEventListener('mousemove', (e) => {
},{
signal: controller.signal
});
}
document.addEventListener('mousedown', callback);
document.addEventListener('mouseup', controller.abort);