AbortSignal:以前我没得选,现在我想中止promise
大家好,我卡颂。
遥想数年前的一次面试,面试官问我:promise有什么缺点?

真是百思不得姐啊...
答案是:promise一旦初始化,就不能中止。这是由promise的实现决定的。
AbortSignal的出现使promise从语义上变为可中止的。并且,只要符合规范,所有异步操作都能变为可中止的。
AbortSignal是什么
AbortSignal是个实验性API,不过兼容性还不错,而且polyfill实现起来也不复杂。

AbortSignal可以实例化一个信号对象(signal object)。
AbortController可以实例化一个信号对象的控制器。
就像遥控器可以发出信号关电视一样,AbortController的实例可以控制中止信号。

只要符合AbortSignal的接入规范,任何异步操作都能实现中止功能。
举个例子,首先new一个控制器实例:
// 控制器实例
const controller = new AbortController();
const signal = controller.signal;
其中signal是控制器对应的信号对象。
信号对象可以监听abort事件,当信号被中止时被触发。
调用controller.abort()方法后会中止信号,此时signal.aborted为true。
// 监听 abort 事件
signal.addEventListener('abort', () => {
console.log("信号中止!")
});
// 控制器中止信号
controller.abort();
console.log('是否中止:', signal.aborted);
如上代码调用后会依次打印:
信号中止! 是否中止:true

在fetch中的应用
fetch API已经集成了AbortSignal。controller内的信号对象作为signal参数传给fetch:const controller = new AbortController();
fetch(url, {
signal: controller.signal
});
controller.abort()后,fetch的promise会变为AbortError DOMException reject:fetch('xxxx', {
signal: controller.signal
}).then(() => {}, err => {
if (err.name == 'AbortError') {
// 中止信号
} else {
// 其他错误
}
})
fetch如何配合AbortSignal实现取消下载与任何异步操作结合
fetch,任何异步操作只要符合如下规范,都可以与AbortError集成:将
AbortSignal(信号对象)作为API的signal参数传入约定如果
API返回的promise变为AbortError DOMException reject则代表操作被中止如果
signal.aborted === true则立刻让promise变为reject观测
AbortSignal状态的变化
API应用场景比较复杂(比如需要考虑多线程通信),文档中提供了一套基于订阅发布的abort-algorithms机制来完成步骤4。
总结
AbortSignal原理很简单,但只要遵守接入规范,他的可扩展性是很强的。signal传给多个符合规范的API,就能用一个控制器中止多个API的调用。
评论
