实现一个带撤销功能的Primise

前言

在Javascript中,我们通常使用 Promise来实现异步功能, 然而, 在一些特定场景下,有时候我们需要这个异步操作能够被取消. 假定我们有一下场景, 用户打开一个对话框的时候我们就开始监听日志,在用户关闭对话框的时候, 不管监听通道是否建立都能关闭掉它. 再或者, 我们向用户发起一个请求. 并一直等待响应, 在任意时候我们能够取消掉它.

实现方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* @param {((resolve: (value: any) => void, reject: (reason?: any) => void,, update: (reason?: any) => void) => void, cancel:() => void} fn
* @returns {PromiseEx}
*/
const PromiseEx = function (fn){
let _fn = [], p, _cancel = null;

this.then = fn=>{p.then(fn); return this}
this.catch = fn=>{p.catch(fn);return this}
this.update = fn=>{_fn.push(fn); return this}
this.cancel = ()=>_cancel && _cancel();

const update = (o)=>_fn.forEach(_cb=>_cb(o)), cancel = (cb)=>{ _cancel = cb}

p = new Promise((resolve, reject)=>fn(resolve, reject, update, cancel));
return this;
}

如何使用

下面这段代码演示了它如何工作. 执行后会每1秒打印一跳任务状态更新消息 100次, 如果当你执行 p.cancel() 任务会被撤销.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
var p = new PromiseEx((resolve, reject, update, cancel)=>{
let times = 0;
let t = setInterval(()=>{
update('任务状态更新消息: ' + times++);
if(times == 100){
clearInterval(t);
resolve('100次任务执行完成');
}
}, 1000)
// ... do something

// 外部调用 p.cancel 的时候这里会被执行.
cancel(()=>{
// canceled
clearInterval(t);
reject('任务被取消')
})
})

p.then(res=>{
console.log('任务执行完成')
}).catch(res=>{
console.log(res)
}).update(res=>{
console.log(res)
})

作者

bywayboy

发布于

2022-04-25

更新于

2022-04-27

许可协议