https://promisesaplus.com/
需求包括:
- 同一个promise允许被多次then/catch
- then/catch函数: then(onFulfilled, onRejected), catch(onRjecte)
- then/catch 返回一个新的promise,因此允许链式调用
- onFulfilled/onReject为null时, 继续将当前result/error往之后的chain传
- 每个onFulfilled/onReject最多被执行一次
- onFulfilled/onReject执行过程中抛出异常时,立即往之后的chain抛异常
- 链尾的异常不应该被之前的promise catch到
- catch之后被恢复: 执行onRjected的返回值作为result
实现要点:
- 如何让同一个promise多次then/catch: 每个promise维护一个defer callbacks队列,每次then时将callback加入队列就好了
- 如何返回新的promise: 将传给then的onFulfilled/onReject改造成
v => next_resolve(onFulfilled(v))
,e => next_resolve(onRjected(e))
- 注意try/catch细节,和处理下onFulfilled/onReject可能返回promise的情况
let isThenable = x => !!(x!==undefined && x.then);
let runThenable = (func, arg) => isThenable(arg) ? arg.then(func) : func(arg);
class MyPromise{
constructor(f){
this.succ_que = [];
this.fail_que = [];
this.done = false;
this.resolve = result => {
if(this.done) return;
this.result = result;
this.done = true;
setImmediate(() => { // setImmediate to prevent children from being caught by parent
this.succ_que.forEach(cb => cb(result));
});
};
this.reject = error => {
if(this.done) return;
this.error = error;
this.done = true;
setImmediate(() => {
this.fail_que.forEach(cb => cb(error));
});
};
this.then = (succ_cb, fail_cb) => new MyPromise((next_resolve, next_reject) => {
let handle_result = v => {
try{
runThenable(next_resolve, succ_cb ? succ_cb(v) : v); // runThenable to allow succ_cb/fail_cb return a promise
} catch (e) {
next_reject(e)
}
};
let handle_error = e => {
try{
if(fail_cb)
runThenable(next_resolve, fail_cb(e)); // resume after caught by fail_cb
else
next_reject(e);
}catch (e) {
next_reject(e)
}
};
if(this.done){
this.error ? handle_error(this.error) : handle_result(this.result)
}else{
this.succ_que.push(handle_result);
this.fail_que.push(handle_error);
}
});
this.catch = fail_cb => this.then(null, fail_cb);
try{
f(this.resolve, this.reject);
}catch (e) {
this.reject(e);
}
}
}
MyPromise.resolve = x => new MyPromise(r => r(x));
MyPromise.Race = (...promises) => {
let done = 0;
return new MyPromise(r => {
promises.forEach(p => p.then(v =>{ if(!done){done++; r(v);} }));
})
};
MyPromise.All = (...promises)=>{
let count = promises.length;
let values = [];
return new MyPromise((r, f) => {
promises.forEach((p, i) =>
p.then(v => {
values[i] = v;
count --;
if(count === 0) r(values);
}, f)
)
});
};
// tests:
// then/catch on the same promise:
let defer = new MyPromise(r => setImmediate(()=>r(100)));
[1,2,3].forEach(plus => defer.then(v => console.log(v+plus))); // 101, 102, 103
let defer_error = new MyPromise((_, rej) => setImmediate(()=>rej(new Error("defer error"))));
[1,2,3].forEach(i => defer_error.catch(e => console.log(i, e.message)));
// chained then/catch:
let p = MyPromise.resolve(1);
p.then(v=> v+1)
.catch(e => console.log(`won't happen error: ${e}`))
.then(v => {console.log(`continued: ${v}`); throw new Error("throw");})
.then(v => {console.log("won't happen then");})
.catch(e => {console.log(`catched: ${e}`); return 100;})
.then(v => {console.log(`continue after catch: ${v}`); return v;})
.then(v => new MyPromise(r=> setTimeout(() => r(v+500), 3000)))
.then(v => console.log(`last: ${v}`))
;
console.log("===========");