手写Promise

623 阅读14分钟

一、promise基础用法

Promise对象是一个构造函数,用来生成Promise实例,promise对象特点:

  • 有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)
  • Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolvereject
  • Promise 的原型上定义着 then 方法。

Promise原生基础实例

const promise = new Promise(function(resolve, reject) {
  // ... some code

  if (/* 异步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});

resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。

promise.then(function(value) {
  // success
}, function(error) {
  // failure
});

实现基础promise

基础框架

class Promise {
    _pending = 'pending';  // 进行在
    _fulfilled = 'fulfilled'; // 完成
    _rejected = 'rejected'; // 失败
    
    // 状态描述 pending fulfilled rejected
    _state = this._pending; // 状态
    value = null; // 保存数据
    reason = null;

    constructor(executor) {
      try {
        // bind避免因为函数外执行,方法内部this指向改变无法
        executor(this.resolve.bind(this), this.reject.bind(this));
      } catch (error) {
        this.reject(error);
      }
    }
    resolve(value) {
    }
    reject(reason) {
    }
  }
  
   Promise.prototype.then = function(onFulfilled, onRejected) {
   
   }

Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolvereject。实现resolve和rejected

class Promise {
    _pending = 'pending';  // 进行在
    _fulfilled = 'fulfilled'; // 完成
    _rejected = 'rejected'; // 失败
    
    // 状态描述 pending fulfilled rejected
    _state = this._pending; // 状态
    value = null; // 保存数据
    reason = null;

    constructor(executor) {
      try {
        // bind避免因为函数外执行,方法内部this指向改变无法
        executor(this.resolve.bind(this), this.reject.bind(this));
      } catch (error) {
        this.reject(error);
      }
    }
   //  `resolve`函数的作用是,将`Promise`对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
    resolve(value) {
      if(this._state === this._pending) {
        this.value = value;
        this._state = this._fulfilled;
      }
    }
    // reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去
    reject(reason) {
      if(this._state === this._pending) {
        this.reason = reason;
        this._state = this._rejected;
      }
    }
  }

实现then

then方法原生实例

promise.then(function(value) {
// success
}, function(error) {
// failure
});

then方法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为resolved时调用,第二个回调函数是Promise对象的状态变为rejected时调用。这两个函数都是可选的,不一定要提供。

代码实现

Promise.prototype.then = function(onFulfilled, onRejected) {
    // 当传入的then回调函数为空的时候。。创建对应的空函数
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : () => {};
    onRejected = typeof onRejected === 'function' ? onRejected : () => {};

    if(this._state === this._fulfilled) {
      onFulfilled(this.value);
    } else if(this._state === this._rejected) {
      onRejected(this.reason);
    }
  }

二、实现异步

同步的

let myPromise = new Promise((resolve, reject) => { resolve(1) });
p.then((data) => console.log(data)) // 1

试下异步

let myPromise = new Promise((resolve, reject) => { 
   setTimeout(() => {
     resolve('resolve1');
   }) 
});
p.then((data) => console.log(data)) // 无输出

因为setTimeout是异步的,在调用then方法时候_state还是pending,不会执行onFulfilled和onRejected。

开始改造: 在执行then方法时如果还在等待态 pending,就把回调函数临时寄存到队列(就是一个数组)里,当状态发生改变时依次从数组中取出执行。

1、新增两个数组,onResolvedCallbacks,onRejectedCallbacks,用于存放成功和失败的回调函数

  class Promise {
    _pending = 'pending';
    _fulfilled = 'fulfilled';
    _rejected = 'rejected';
    _state = this._pending; // 状态
    value = null; // 保存数据
    reason = null;
    
     // 保存尚未fulfilled的then中的回调函数(异步)
    onResolvedCallbacks = [];
    // 保存尚未rejected的then中的回调函数(异步)
    onRejectedCallbacks = [];
    // ...
    }

2、then方法执行时如果状态是等待态,就将其回调函数存入对应数组

Promise.prototype.then = function(onFulfilled, onRejected) {
   // 当传入的then回调函数为空的时候。。创建对应的空函数
   onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : () => {};
   onRejected = typeof onRejected === 'function' ? onRejected : () => {};

   if(this._state === this._fulfilled) {
     onFulfilled(this.value);
   } else if(this._state === this._rejected) {
     onRejected(this.reason);
   } else if(this._state === this._pending) { // 新增等待态判断,此时异步代码还未走完,回调入数组队列
     this.onResolvedCallbacks.push(onFulfilled);
     this.onRejectedCallbacks.push(onRejected);
   }
 }

3、resolve 和 reject 方法中调用onResolvedCallbacks,onRejectedCallbacks

 resolve(value) {
    if(this._state === this._pending) {
      this.value = value;
      this._state = this._fulfilled;
      // 判断成功回调是否存在,如果存在就调用
      // 循环回调数组. 把数组前面的方法弹出来并且直接调用
      // shift方法是在数组中删除值,每执行一个就删除一个,最终变为0
      while(this.onResolvedCallbacks.length) {
        this.onResolvedCallbacks.shift()(value);
      }
    }
  }
  reject(reason) {
    if(this._state === this._pending) {
      this.reason = reason;
      this._state = this._rejected;
      // 判断失败回调是否存在,如果存在就调用
      // 循环回调数组. 把数组前面的方法弹出来并且直接调用
      while(this.onRejectedCallbacks.length) {
        this.onRejectedCallbacks.shift()(reason);
      }
    }
  }

这样已经可以输出了异步的数据了。

但是加上执行顺序,就出问题了

console.log(1)
function action() {
 console.log(2)
 new Promise((resolve, reject) => {
   console.log(3)
    resolve('resolve1');

 }).then((data) => {
   console.log('then:' + data)
 })
 console.log('jieshu')
}
action();

实际输出: 1->2->3->then:resolve1->jieshu;

本来应该输出: 1->2->3->jieshu->jieshu;

4、实现resolverejected异步,使用queueMicrotask

resolve(value) {
    if(this._state !== this._pending) return;
    // resolve实现异步(微任务)
    queueMicrotask(() => {
        this.value = value;
        this._state = this._fulfilled;

          // 判断成功回调是否存在,如果存在就调用
          // 循环回调数组. 把数组前面的方法弹出来并且直接调用
          // shift方法是在数组中删除值,每执行一个就删除一个,最终变为0
          while(this.onResolvedCallbacks.length) {
            this.onResolvedCallbacks.shift()(value);
          }
          // 遍历执行成功回调
          // this.onResolvedCallbacks.forEach(callBack => callBack(value))
    // })
  }
  // reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去
  reject(reason) {
    if(this._state !== this._pending) return;
    // 实现异步(微任务)
    queueMicrotask(() => {
      this.reason = reason;
      this._state = this._rejected;
      // 判断失败回调是否存在,如果存在就调用
      // 循环回调数组. 把数组前面的方法弹出来并且直接调用
      while(this.onRejectedCallbacks.length) {
        this.onRejectedCallbacks.shift()(reason);
      }
      // 遍历执行失败回调
      // this.onRejectedCallbacks.forEach(callBack => callBack(reason));
    })
  }

三、实现链式调用then

then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。

then的需求是什么,这需要仔细看 Promises/A+ 规范中对then方法的返回值定义及 Promise 解决过程,如下:

  • 首先then 方法必须返回一个 promise 对象(划重点)
  • 如果then方法中返回的是一个普通值(如 Number、String 等)就使用此值包装成一个新的 Promise 对象返回
  • 如果then方法中没有return语句,就返回一个用 Undefined 包装的 Promise 对象
  • 如果then方法中出现异常,则调用失败态方法(reject)跳转到下一个then的 onRejected
  • 如果then方法没有传入任何回调,则继续向下传递(值穿透)
  • 如果then方法中返回了一个 Promise 对象,那就以这个对象为准,返回它的结果

1、then返回一个新的promise

Promise.prototype.then = function(onFulfilled, onRejected) {
    // 当传入的then回调函数为空的时候。。创建对应的空函数,当then方法中没有回调时,我们需要把接收到的值继续向下传递
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (value) => value;
    onRejected = typeof onRejected === 'function' ? onRejected : (err) => { throw err};
    // then方法会返回一个新的promise对象,以便使用then方法接收结果
    return new Promise((resolve, reject) => {
      // 封装一个成功时执行的函数
      let fulfilled = value => {
        try {
          // 拿到返回值resolve出去
          let result = onFulfilled(value);
          resolve(result);
        } catch (error) {
          reject(error)
        }
      };

      // 封装一个失败时执行的函数
      let rejected = err => {
        try {
          // 拿到返回值reject出去
          let result = onRejected(value);
          reject(result);
        } catch (error) {
          reject(error)
        }
      };

      if(this._state === this._fulfilled) {
        fulfilled(this.value);
      } else if(this._state === this._rejected) {
        rejected(this.reason);
      } else if(this._state === this._pending) { // 新增等待态判断,此时异步代码还未走完,回调入数组队列
        this.onResolvedCallbacks.push(fulfilled);
        this.onRejectedCallbacks.push(rejected);
      }

    })

2、then方法如果返回一个promise,要等返回的promise状态变化后才执行下面的then

// 判断是否是Promise
    isPromise(promiseNew, callValue, resolve, reject) {
        // 如果相等了,说明return的是自己,抛出类型错误并返回
        if(promiseNew === callValue) {
            return reject('请避免Promise循环引用')
        }
        if(callValue instanceof Promise) {
            // 如果当前回调函数返回Promise对象,必须等待其状态改变后在执行下一个回调
            // 调用上一次then中成功回调返回的型promise的then方法
            callValue.then(resolve, reject);
        } else {
          // 如果是一个非Promise数值,那么就直接调用返回 ---- promise特性
            resolve(callValue);
        }
    }

tthen方法调用isPromise

Promise.prototype.then = function(onFulfilled, onRejected) {
  // 当传入的then回调函数为空的时候。。创建对应的空函数,当then方法中没有回调时,我们需要把接收到的值继续向下传递
  onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (value) => value;
  onRejected = typeof onRejected === 'function' ? onRejected : (err) => { throw err};
  // then方法会返回一个新的promise对象,以便使用then方法接收结果
  let promiseNew = new Promise((resolve, reject) => {
    // 封装一个成功时执行的函数
    let fulfilled = value => {
      try {
        // 拿到返回值resolve出去
        let result = onFulfilled(value);
        // 判断是否是Promise
        /*
          判断返回的是否是promise,这里的resolve,reject是
          返回的新promise的成功/失败的接收函数
        */
        this.isPromise(promiseNew, result, resolve, reject);
      } catch (error) {
        reject(error)
      }
    };

    // 封装一个失败时执行的函数
    let rejected = err => {
      try {
        // 拿到返回值reject出去
        let result = onRejected(err);
        this.isPromise(promiseNew,result, resolve, reject);
      } catch (error) {
        reject(error)
      }
    };
    if(this._state === this._fulfilled) {
      fulfilled(this.value);
    } else if(this._state === this._rejected) {
      rejected(this.reason);
    } else if(this._state === this._pending) { // 新增等待态判断,此时异步代码还未走完,回调入数组队列
      this.onResolvedCallbacks.push(fulfilled);
      this.onRejectedCallbacks.push(rejected);
    }

  })

  return promiseNew;

}

四、catch实现

Promise.prototype.catch()方法是.then(null, rejection).then(undefined, rejection)的别名,用于指定发生错误时的回调函数。

Promise.prototype.catch = function(onRejected) {
 return this.then(null, onRejected)
}

五、Promise.resove(),reject()

  • 如果参数就是一个promise对象,直接返回,如果是一个值,那么需要生成一个promise对象,把值进行返回
  • 是Promise类的一个静态方法
class Promise {
    ...//
    static resolve(callBack) {
      let P = this.constructor;  // Promise
      if(callBack instanceof P) {
        // 如果是promise对象,就直接返回
        return callBack
      } else {
          // 如果是值就返回一个promise对象,并返回值
        return new Promise(resolve => resolve(callBack));
      }

    }
        
        ...//
}

Promise.reject()的实现

static reject(callBack) {
      let P = this.constructor;  // Promise
      if(callBack instanceof P) {
        return callBack
      } else {
        return new Promise((resolve,reject) => reject(callBack));
      }
    }

六、finally实现

不管promise最后的状态,在执行完thencatch指定的回调函数以后,都会执行finally方法指定的回调函数。

  • 无论当前最终状态是成功还是失败,finally都会执行
  • 我们可以在finally方法之后调用then方法拿到结果
  • 这个函数是在原型对象上用的
Promise.prototype.finally = function(callback)  {
     // 如何拿到当前的promise的状态,使用then方法,而且不管怎样都返回callback
    // 而且then方法就是返回一个promise对象,那么我们直接返回then方法调用之后的结果即可
    // 我们需要在回调之后拿到成功的回调,所以需要把value也return
    // 失败的回调也抛出原因
    // 如果callback是一个异步的promise对象,我们还需要等待其执行完毕,所以需要用到静态方法resolve
    let P = this.constructor;  // Promise
    return this.then(
      value  => P.resolve(callback()).then(() => value),
      reason => P.resolve(callback()).then(() => { throw reason })
    );
  }

七、all方法

Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。

Promise.all()方法接受一个数组作为参数,数组元素都是 Promise 实例,如果不是,就会先调用Promise.resolve方法,将参数转为 Promise 实例。

// Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例
   static all(list) {
       // 返回新的Promise
       return new Promise((resolve, reject) => {
           let result = []; //存储结果
           for(let [i, elem] of list.entries()) {
               // Promise.all()方法接受一个数组作为参数,参数都是promise实例,如果不是,就会先调用下面讲到的Promise.resolve方法,将参数转为 Promise 实例
               let p = elem instanceof Promise ? elem : Promise.resolve(elem);
               p.then(res => {
                   result.push(res);
                   // 全部resolve时返回的Promise状态就变成resolve
                   if(result.length === list.length) {
                       resolve(result);
                   }
               }, err => {
                   // 有一个被rejected时返回的Promise状态就变成rejected
                   reject(err)
               })
           }
       })
   }

八、race

Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例

// Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例
 static race(list) {
     // 返回新的Promise
     return new Promise((resolve, reject) => {
         for(let [i, elem] of list.entries()) {
             // Promise.race()方法接受一个数组作为参数,参数都是promise实例,如果不是,就会先调用下面讲到的Promise.resolve方法,将参数转为 Promise 实例
             let p = elem instanceof Promise ? elem : Promise.resolve(elem);
             p.then(res => {
                 // 有一个实例resolve时返回的Promise状态就变成resolve
                 return resolve(res);
             }, err => {
                 // 有一个被rejected时返回的Promise状态就变成rejected
                 reject(err)
             })
         }
     })
 }

九、全部代码

class Promise {
 _pending = 'pending';
 _fulfilled = 'fulfilled';
 _rejected = 'rejected';
 _state = this._pending; // 状态
 value = null; // 保存数据
 reason = null;
  // 保存尚未fulfilled的then中的回调函数(异步)
 onResolvedCallbacks = [];
 // 保存尚未rejected的then中的回调函数(异步)
 onRejectedCallbacks = [];

 constructor(executor) {
   try {
     // bind避免因为函数外执行,方法内部this指向改变无法
     executor(this.resolve.bind(this), this.reject.bind(this));
   } catch (error) {
     this.reject(error);
   }
 }
 // resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
 resolve(value) {
   if(this._state !== this._pending) return;
   // resolve实现异步(微任务)
   queueMicrotask(() => {
       this.value = value;
       this._state = this._fulfilled;

         // 判断成功回调是否存在,如果存在就调用
         // 循环回调数组. 把数组前面的方法弹出来并且直接调用
         // shift方法是在数组中删除值,每执行一个就删除一个,最终变为0
         while(this.onResolvedCallbacks.length) {
           this.onResolvedCallbacks.shift()(value);
         }

   })
 }
 // reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去
 reject(reason) {
   if(this._state !== this._pending) return;
   // 实现异步(微任务)
   queueMicrotask(() => {
     this.reason = reason;
     this._state = this._rejected;
     // 判断失败回调是否存在,如果存在就调用
     // 循环回调数组. 把数组前面的方法弹出来并且直接调用
     while(this.onRejectedCallbacks.length) {
       this.onRejectedCallbacks.shift()(reason);
     }

   })
 }

 // 判断是否是Promise
 isPromise(promiseNew, callValue, resolve, reject) {
     // 如果相等了,说明return的是自己,抛出类型错误并返回
     if(promiseNew === callValue) {
         return reject('请避免Promise循环引用')
     }
     if(callValue instanceof Promise) {
         // 如果当前回调函数返回Promise对象,必须等待其状态改变后在执行下一个回调
         // 调用上一次then中成功回调返回的型promise的then方法
         callValue.then(resolve, reject);
     } else {
       // 如果是一个非Promise数值,那么就直接调用返回 ---- promise特性
         resolve(callValue);
     }
 }

 static resolve(callBack) {
   let P = this.constructor;  // Promise
   if(callBack instanceof P) {
     // 如果是promise对象,就直接返回
     return callBack
   } else {
       // 如果是值就返回一个promise对象,并返回值
     return new Promise(resolve => resolve(callBack));
   }

 }

 static reject(callBack) {
   let P = this.constructor;  // Promise
   if(callBack instanceof P) {
     return callBack
   } else {
     return new Promise((resolve,reject) => reject(callBack));
   }
 }

// Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例
 static all(list) {
     // 返回新的Promise
     return new Promise((resolve, reject) => {
         let result = []; //存储结果
         for(let [i, elem] of list.entries()) {
             // Promise.all()方法接受一个数组作为参数,参数都是promise实例,如果不是,就会先调用下面讲到的Promise.resolve方法,将参数转为 Promise 实例
             let p = elem instanceof Promise ? elem : Promise.resolve(elem);
             p.then(res => {
                 result.push(res);
                 // 全部resolve时返回的Promise状态就变成resolve
                 if(result.length === list.length) {
                     resolve(result);
                 }
             }, err => {
                 // 有一个被rejected时返回的Promise状态就变成rejected
                 reject(err)
             })
         }
     })
 }

 // Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例
 static race(list) {
     // 返回新的Promise
     return new Promise((resolve, reject) => {
         for(let [i, elem] of list.entries()) {
             // Promise.race()方法接受一个数组作为参数,参数都是promise实例,如果不是,就会先调用下面讲到的Promise.resolve方法,将参数转为 Promise 实例
             let p = elem instanceof Promise ? elem : Promise.resolve(elem);
             p.then(res => {
                 // 有一个实例resolve时返回的Promise状态就变成resolve
                 return resolve(res);
             }, err => {
                 // 有一个被rejected时返回的Promise状态就变成rejected
                 reject(err)
             })
         }
     })
 }

}

Promise.prototype.then = function(onFulfilled, onRejected) {
 // 当传入的then回调函数为空的时候。。创建对应的空函数,当then方法中没有回调时,我们需要把接收到的值继续向下传递
 onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (value) => value;
 onRejected = typeof onRejected === 'function' ? onRejected : (err) => { throw err};
 // then方法会返回一个新的promise对象,以便使用then方法接收结果
 let promiseNew = new Promise((resolve, reject) => {
   // 封装一个成功时执行的函数
   let fulfilled = value => {
     try {
       // 拿到返回值resolve出去
       let result = onFulfilled(value);
       // 判断是否是Promise
       /*
         判断返回的是否是promise,这里的resolve,reject是
         返回的新promise的成功/失败的接收函数
       */
       this.isPromise(promiseNew, result, resolve, reject);
     } catch (error) {
       reject(error)
     }
   };

   // 封装一个失败时执行的函数
   let rejected = err => {
     try {
       // 拿到返回值reject出去
       let result = onRejected(err);
       this.isPromise(promiseNew,result, resolve, reject);
     } catch (error) {
       reject(error)
     }
   };
   if(this._state === this._fulfilled) {
     fulfilled(this.value);
   } else if(this._state === this._rejected) {
     rejected(this.reason);
   } else if(this._state === this._pending) { // 新增等待态判断,此时异步代码还未走完,回调入数组队列
     this.onResolvedCallbacks.push(fulfilled);
     this.onRejectedCallbacks.push(rejected);
   }

 })

 return promiseNew;

}

Promise.prototype.catch = function(onRejected) {
 return this.then(null, onRejected)
}

Promise.prototype.finally = function(callback)  {
  // 如何拿到当前的promise的状态,使用then方法,而且不管怎样都返回callback
 // 而且then方法就是返回一个promise对象,那么我们直接返回then方法调用之后的结果即可
 // 我们需要在回调之后拿到成功的回调,所以需要把value也return
 // 失败的回调也抛出原因
 // 如果callback是一个异步的promise对象,我们还需要等待其执行完毕,所以需要用到静态方法resolve
 let P = this.constructor;  // Promise
 return this.then(
   value  => P.resolve(callback()).then(() => value),
   reason => P.resolve(callback()).then(() => { throw reason })
 );
}

参考: