【分享】如何优雅地中断 Promise?来试试 AbortController 吧!
早上摸鱼时发现的,头次知道(https://developer.mozilla.org/zh-CN/docs/Web/API/AbortController#%E6%B5%8F%E8%A7%88%E5%99%A8%E5%85%BC%E5%AE%B9%E6%80%A7),可以中断Promise,觉得不错,分享一下浏览器兼容性也可以,考虑后续脚本猫后台脚本的中断就采用这个方案
原文:https://mp.weixin.qq.com/s/liTMB0sTcl6jkEjUPzCUjA 这是中断fetch的吧 李恒道 发表于 2022-9-15 10:03
这是中断fetch的吧
Promise也可以,只是得手动实现?更多应该是一个规范,用它提供的接口去判断
```js
/**
* 自定义的可以主动取消的 Promise
*/
function myCoolPromise ({ signal }) {
return new Promise((resolve, reject) => {
// 如果刚开始 signal 存在并且是终止的状态可以直接抛出异常
signal?.throwIfAborted();
// 异步的操作,这里使用 setTimeout 模拟
setTimeout(() => {
Math.random() > 0.5 ? resolve('ok') : reject(new Error('not good'));
}, 1000);
// 添加 abort 事件监听,一旦 signal 状态改变就将 Promise 的状态改变为 rejected
signal?.addEventListener('abort', () => reject(signal?.reason));
});
}
/**
* 使用自定义可取消的 Promise
*/
const ac = new AbortController();
const { signal } = ac;
myCoolPromise({ signal }).then((res) => console.log(res), err => console.warn(err));
setTimeout(() => {
ac.abort();
}, 100); // 可以更改时间看不同的结果
``` AbortController中断Promise的效果其实没有那么理想,他只是提前resolve/reject,如果不做任何处理,Promise实际还是在运行的:
function myPromise(signal) { // 吐槽下示例的写法,额外创建了一个对象,还不如把ac传进来算了
return new Promise(resolve => {
let i = 0;
const timer = setInterval(() => {
console.log(++i);
if (i === 10) {
clearInterval(timer);
resolve('done');
}
}, 500);
signal.addEventListener('abort', () => resolve('aborted'));
});
}
const ac = new AbortController();
const { signal } = ac;
myPromise(signal).then(e => console.log(e));
setTimeout(() => {
ac.abort();
}, 2000);
这个例子可以看到,中断后定时器还是在继续执行,这与Promise的特性有关,要正确中断必须自己加clearInterval才行。AbortController只是抛出一个中断信号,具体的中断实现和收尾工作还得自己写,换个角度说,即使不用AbortController,这种“中断”特性也可以轻松实现:
function myPromise(signal) {
return new Promise(resolve => {
let i = 0;
const timer = setInterval(() => {
console.log(++i);
if (i === 10) {
clearInterval(timer);
resolve('done');
}
}, 500);
signal.abort = () => {
clearInterval(timer);
resolve('aborted');
}
});
}
const signal = {};
myPromise(signal).then(e => console.log(e));
setTimeout(() => {
signal.abort();
}, 2000);
至于中断fetch和addEventListener,实际也是因为这些API内部实现了中断逻辑(有一定兼容性问题)。中断Promise听上去很高大上,其实也就那回事,就是同时中断多个比较方便而已。 cxxjackie 发表于 2022-9-15 12:06
AbortController中断Promise的效果其实没有那么理想,他只是提前resolve/reject,如果不做任何处理,Promis ...
是的,还是得自己手动处理
优点就是这是规范,很多地方都有用 试着手写了一下AbortController的实现,感觉这玩意是真的简单:
class AbortSignal extends EventTarget {
constructor() {
super();
this.aborted = false;
this.reason = undefined;
this.onabort = null;
}
throwIfAborted() {
if (this.aborted) throw this.reason;
}
}
class AbortController {
constructor() {
this.signal = new AbortSignal();
}
abort(reason = new DOMException('signal is aborted without reason')) {
this.signal.aborted = true;
this.signal.reason = reason;
const evt = new Event('abort');
this.signal.dispatchEvent(evt);
if (typeof this.signal.onabort === 'function') this.signal.onabort(evt);
}
} cxxjackie 发表于 2022-9-15 21:22
试着手写了一下AbortController的实现,感觉这玩意是真的简单:
话说这手写的,fetch能用么? 王一之 发表于 2022-9-15 21:25
话说这手写的,fetch能用么?
不行,过不了类型判断,fetch应该有真正的AbortSignal引用。这个AbortSignal目前应该算实验性质的接口,没有提供实例化方法(只能实例化一个已中断的signal),所以我才重新写了一个,以后可能可以直接new吧。
页:
[1]