美文网首页
实现 JS promise

实现 JS promise

作者: davidhuangdw | 来源:发表于2019-05-16 15:59 被阅读0次

https://promisesaplus.com/
需求包括:

  1. 同一个promise允许被多次then/catch
  2. 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

实现要点:

  1. 如何让同一个promise多次then/catch: 每个promise维护一个defer callbacks队列,每次then时将callback加入队列就好了
  2. 如何返回新的promise: 将传给then的onFulfilled/onReject改造成v => next_resolve(onFulfilled(v)), e => next_resolve(onRjected(e))
  3. 注意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("===========");

相关文章

网友评论

      本文标题:实现 JS promise

      本文链接:https://www.haomeiwen.com/subject/ttmiaqtx.html