AbortSignal:以前我没得选,现在我想中止promise

SegmentFault

共 2126字,需浏览 5分钟

 ·

2021-10-09 19:25

作者:卡颂
来源:SegmentFault 思否社区


大家好,我卡颂。

遥想数年前的一次面试,面试官问我: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.abortedtrue

// 监听 abort 事件
signal.addEventListener('abort', () => {
  console.log("信号中止!")
});

// 控制器中止信号
controller.abort(); 

console.log('是否中止:', signal.aborted); 

如上代码调用后会依次打印:

  1. 信号中止!
  2. 是否中止:true


在fetch中的应用

fetch API已经集成了AbortSignal
只需要将controller内的信号对象作为signal参数传给fetch
const controller = new AbortController();
fetch(url, {
  signal: controller.signal
});
当调用controller.abort()后,fetchpromise会变为AbortError DOMException reject
fetch('xxxx', {
  signal: controller.signal
}).then(() => {}, err => {
  if (err.name == 'AbortError') { 
    // 中止信号
  } else {
    // 其他错误
  }
})
可以在此时处理中止后的操作。
这里有个取消视频下载Demo,可以看看fetch如何配合AbortSignal实现取消下载

与任何异步操作结合

不仅是fetch,任何异步操作只要符合如下规范,都可以与AbortError集成:
  1. AbortSignal(信号对象)作为APIsignal参数传入

  2. 约定如果API返回的promise变为AbortError DOMException reject则代表操作被中止

  3. 如果signal.aborted === true则立刻让promise变为reject

  4. 观测AbortSignal状态的变化

如果API应用场景比较复杂(比如需要考虑多线程通信),文档中提供了一套基于订阅发布abort-algorithms机制来完成步骤4。


总结

虽然AbortSignal原理很简单,但只要遵守接入规范,他的可扩展性是很强的。
比如,可以将一个signal传给多个符合规范的API,就能用一个控制器中止多个API的调用。
就像一个遥控器,同时操作家里的空调、电视、洗衣机,你爱了么?


点击左下角阅读原文,到 SegmentFault 思否社区 和文章作者展开更多互动和交流,扫描下方”二维码“或在“公众号后台回复“ 入群 ”即可加入我们的技术交流群,收获更多的技术文章~

- END -


浏览 16
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报