大概是更简单易懂的手写promise(代码量也更短)

137 阅读3分钟

网上的promise手写项目已经有很多了, 不过看了几个基本都是同一个版本改出来的, 如juejin.cn/post/684490…, 都是手动的调用回调. 现在用自己的思路手写一个, 核心是proxy(也可以换成Object.defineProperty), 使用设置回调的方式来保证传递执行.

/**
 * 手写promise的关键一是在于对resolve, reject的处理, 二在于对状态的保存
 * 最重要的是then,catch,finally的传递
 * 我们的promise 按照规范来写 就可以和别人的promise公用
 */

 const pending = Symbol('pending');
 const rejected = Symbol('rejected');
 const fulfilled = Symbol('fulfilled');
 let id = 0;
 class Promise {
   constructor (executer) {
     if (typeof executer !== 'function') {
       throw TypeError(`Promise resolver ${typeof executer} is not a function`);
     }
     // 2.2.6.1: If/when `promise` is fulfilled, all respective `onFulfilled` callbacks must execute in the order of their originating calls to `then`.
     // 允许注册多个executer
     this.executerFns = [];
     this.ctx = new Proxy({ state: pending, value: undefined, error: undefined, uid: id++ }, {
       set: (target, key, value) => {
         if (key === 'state') {
           if (Reflect.get(target, key) === pending && value !== pending) {
             // console.log('set state:', value, ', pre state:', Reflect.get(target, key), ', id:', this.ctx.uid,);
             Reflect.set(target, key, value);
             
             // 这里调用
             // 2.2.2.2 立即执行时会导致当前方法中resolve后的内容无法执行
             queueMicrotask(() => {
               // console.log('run executer', this.ctx.state, ', id:', this.ctx.uid, ', executers length: ', this.executerFns.length);
               while (this.executerFns.length > 0) {
                 let executer = this.executerFns.shift();
                 executer();
               }
             });
           }
         } else {
           Reflect.set(target, key, value);
         }
         return true;
       }
     })
     const resolve = (val) => {
       if (val === this) {
         // 2.3.1: If `promise` and `x` refer to the same object, reject `promise` with a `TypeError' as the reason.
         throw new TypeError('can not return current promise instance! rules: promises-aplus 2.3.1');
       }
       // console.log('resolve val: ', val, this.ctx.state)
       if(val instanceof Promise){ // 是promise 就继续递归解析
           return val.then(resolve, reject)
       } else if ((typeof val === 'object' && val !== null) || typeof val === 'function') {
         let called;
         try {
           // 规范 2.3.3.1
           let then = val.then;
           if (typeof then == 'function') {
             // console.log('then.call: ', val);
             return then.call(val, function (val) {
                 if (called) return;
                 called = true;
                 resolve(val);
               }, function (reason) {
                 if (called) return;
                 called = true;
                 reject(reason);
               });
           } else {
             // resolve(val); // 此时x 就是一个普通对象
             // console.log('resolve ', val);
             if (this.ctx.state === pending) {
               this.ctx.value = val;
               this.ctx.state = fulfilled;
               return;
             }
           }
         } catch (e) {
           if (called) return;
           called = true;
           // 规范 2.3.3.3.4 
           reject(e); // 取then时抛出错误了
           return ;
         }
       }
       // console.log('resolve ', val);
       if (this.ctx.state === pending) {
         this.ctx.value = val;
         this.ctx.state = fulfilled;
       }
     }
   
     const reject = (e) => {
       if (this.ctx.state === pending) {
         this.ctx.error = e;
         this.ctx.state = rejected;
       }
     }
     try {
       executer(resolve, reject);
     } catch(e) {
       reject(e);
     }
   }
 
   // .then
   /**
    * 构造一个callback用于new promise, 这个callback在new promise的时候仅仅是新建操作, 不会执行到传入的回调
    * callback要支持使用前一个promise的状态进行执行操作, 前一个promise怎么传进来?
    * 不能马上执行时, 下一次执行状态在前一个promise执行完成时主动调用
    * callback 要支持原有的Promise
    * then的返回值必然是一个Promise实例, 另外有一点注意的是, then的参数是没有resolve/reject的, 所以不必考虑入参
    * 需要注意的是, onfulfilled/onrejected本身也支持返回一个promise
   */
   then (onfulfilled, onrejected) {
     // 处理undefined情况
     // console.log('onfulfilled is Function', typeof onfulfilled === 'function');
     onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : val => val;
     onrejected = typeof onrejected === 'function' ? onrejected : err => { throw err };
     let cb = (resolve, reject) => {
       // 这时候保证已经进入了resolve/reject状态
       if (this.ctx.state !== pending) {
         // settimeout: 2.2.4 `onFulfilled` or `onRejected` must not be called until the execution context stack contains only platform code.
         queueMicrotask(() => {
           try {
             let value;
             if (this.ctx.state === fulfilled) {
               value = onfulfilled(this.ctx.value);
             } else if (this.ctx.state === rejected) {
               value = onrejected(this.ctx.error);
             }
             resolve(value); // 这里会继续下一个执行链条
           } catch (err) {
             reject(err);
           }
         });
       } else {
         // 返回一个闭包函数
         const executer = () => {
           if (this.ctx.state !== pending) {
             try {
               let value;
               if (this.ctx.state === fulfilled) {
                 value = onfulfilled(this.ctx.value);
               } else if (this.ctx.state === rejected) {
                 value = onrejected(this.ctx.error);
               }
               // console.log('## resolve from executer');
               resolve(value); // 这里会继续下一个执行链条
             } catch (err) {
               // console.log('## reject from executer');
               reject(err);
             }
           }
         }
         // console.log('## add executer to uid: ', this.ctx.uid, ', state: ', this.ctx.state);
         this.executerFns.push(executer);
       }
     }
     let promise2 = new Promise(cb); // 如果当前已经完成了, 则直接执行, 否则绑定, 然后在resolve中执行
     return promise2;
   }
 
   // catch 本质上就是一个只有onrejected的then
   catch(onrejected) {
     return this.then(undefined, onrejected);
   }
 
   // finally操作, 返回值是一个promise实例
   // 本质上也可以当做是一个另类的then
   finally(onfinal) {
     onfinal = typeof onfinal === 'function' ? () => { onfinal(); } : () => {};
     return this.then(onfinal, onfinal);
   }

 }
 
 Promise.deferred = function () {
   let dfd = {};
   dfd.promise = new Promise((resolve,reject)=>{
       dfd.resolve = resolve;
       dfd.reject = reject
   })
   return dfd;
 }
 
 module.exports = Promise