手写Promise源码深入了解其原理

1,076 阅读6分钟

想了解JS Promise具体怎么使用,请看【ES6基础知识】promise和await/async

为了更深入了解其原理,下面动手重写Promise源码

如有不对,欢迎指正。此文适合自己手动实践,能让自己功力大增。

重写MyPromise,基于它再重写then、catch、resolve、reject、finally

MyPromise

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

function MyPromise(executor) {
  let self = this;
  this.status = PENDING;
  this.onFulfilled = [];//成功的回调
  this.onRejected = []; //失败的回调
  function resolve(value) {
    if (self.status === PENDING) {
      self.status = FULFILLED;
      self.value = value;
      // 
      self.onFulfilled.forEach(fn => fn()); //PENDING状态执行,如果前面多个then都是PENDING状态,就是数组,最后面一个then是FULFILLED状态,就是执行数组
    }
  }
  function reject(reason) {
    if (self.status === PENDING) {
      self.status = REJECTED;
      self.reason = reason;
      self.onRejected.forEach(fn => fn());
    }
  }
  try {
    executor(resolve, reject);
  } catch (e) {
    reject(e);
  }
}

then

then支持链式调用,所以要用数组,then中根据状态来控制所执行的内容

MyPromise.prototype.then = function (onFulfilled, onRejected) {
  // onFulfilled 和 onRejected必须是函数类型
  onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
  onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason };
  let self = this;
  let promiseInstance = new MyPromise(
    (resolve, reject) => {
      // then中执行函数(onFulfilled, onRejected)是微任务
      if (this.status === FULFILLED) {
        // 不会变成其它状态
        setTimeout(() => {
          try {
            // 必须在promise变成 fulfilled 时,调用 onFulfilled,参数是promise的value
            // 在promise的状态不是 fulfilled 之前,不能调用
            // onFulfilled 只能被调用一次
            let x = onFulfilled(self.value);
            resolvePromise(promiseInstance, x, resolve, reject);
          } catch (error) {
            reject(e);
          }
        })
      } else if (self.status === REJECTED) {
        // 不会变成其它状态
        setTimeout(() => {
          try {
            let x = onRejected(self.reason);
            resolvePromise(promiseInstance, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        });
      } else if (self.status === PENDING) {
        // 可以变成 fulfilled 或者是 rejected
        // then方法可能被多次调用
        // 如果promise变成了 fulfilled态,所有的onFulfilled回调都需要按照then的顺序执行
        // 如果promise变成了 rejected态,所有的onRejected回调都需要按照then的顺序执行
        self.onFulfilled.push(() => {
          setTimeout(() => {
            try {
              let x = onFulfilled(self.value);
              resolvePromise(promiseInstance, x, resolve, reject);
            } catch (e) {
              // 如果 onFulfilled执行时抛出异常e,promiseInstance需要被reject
              reject(e);
            }
          });
        });
        self.onRejected.push(() => {
          setTimeout(() => {
            try {
              let x = onRejected(self.reason);
              resolvePromise(promiseInstance, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          });
        });
      }
    }
  )
  //  then必须返回一个promise
  return promiseInstance
}
function resolvePromise(promiseInstance, x, resolve, reject) {
  let self = this;
  if (promiseInstance === x) {
    reject(new TypeError('Chaining cycle'));
  }
  // 如果 x 是一个 object 或者 是一个 function
  if (x && typeof x === 'object' || typeof x === 'function') {
    let used; //只能调用一次
    // 
    try {
      // 有.then
      let then = x.then;
      if (typeof then === 'function') {
        // 如果 then 是一个函数,then.call(x, resolvePromiseFn, rejectPromiseFn)
        then.call(x, (y) => {
          // // 调用then函数的resolvePromiseFn
          if (used) return;
          used = true;
          // then是链式,所以后面then执行的函数一样
          resolvePromise(promiseInstance, y, resolve, reject);
        }, (r) => {
          // 调用then函数的rejectPromiseFn
          if (used) return;
          used = true;
          reject(r);
        });
      } else {
        // 不是函数,直接fulfill
        if (used) return;
        used = true;
        resolve(x);
      }
    } catch (e) {
      // x.then出错
      if (used) return;
      used = true;
      reject(e);
    }
  } else {
    //  x 不是一个 object 或者 是一个 function
    resolve(x);
  }
}

catch

catch底层调用的是then,只不过resolved不执行

MyPromise.prototype.catch = function (onRejected) {
  this.then(null, onRejected);
}

resolve

  1. MyPromise.resolve返回一个以给定值解析后的Promise 对象
  2. 如果 value 是个 thenable 对象,返回的promise会“跟随”这个thenable的对象,采用它的最终状态
  3. 如果传入的value本身就是promise对象,那么Promise.resolve将不做任何修改、原封不动地返回这个promise对象
  4. 其他情况,直接返回以该值为成功状态的promise对象。
MyPromise.resolve = function (param) {
  if (param instanceof MyPromise) {
    return param;
  }
  return new MyPromise((resolve, reject) => {
    if (param && param.then && typeof param.then === 'function') {
      setTimeout(() => {
        param.then(resolve, reject);
      });
    } else {
      resolve(param);
    }
  });
}

reject

参数会原封不动地作为reject的理由,变成后续方法的参数。

MyPromise.reject = function (reason) {
  return new MyPromise((resolve, reject) => {
    reject(reason);
  });
}

finally

finally底层调用的是then,FULFILLED和REJECTED都会执行,并且还能继续then,还会将值原封不动的传递给后面的then,所以返回的是一个Promise.resolve(返回Promise.resolve的原因是不管FULFILLED和REJECTED都会执行一个函数,并且还能把值往后传继续then)

MyPromise.prototype.finally = function (callback) {
  return this.then((value) => {
    return MyPromise.resolve(callback()).then(() => {
      return value;
    });
  }, (err) => {
    return MyPromise.resolve(callback()).then(() => {
      throw err;
    });
  });
}

实例

const myPromise1 = (time, res) => new MyPromise(resolve => {
  setTimeout(() => { resolve(res) }, time);
})

const myPromise2 = (time, res) => new MyPromise((resolve, reject) => {
  throw new Error('async error!')
  reject(new Error('test'));   //有了throw,此处不会执行
})

myPromise1(600, 'myPromise1').then((res) => { 
  console.log(res)  //myPromise1
}).catch((err) => {
  console.log('final catch error:', err)
})

> myPromise1

myPromise2(600, 'myPromise2').then((res) => { 
  console.log(res) 
}).catch((err) => {
  console.log('final catch error:', err)
})

>
 final catch error: Error: async error!
 at .../Promise.js:407:9

使用原生Promise重写all、race、allSettled、any、finally

Promise类公共代码

class PromiseCls {
  constructor() {
    this.tasks = [];
  }
  addAsync(task){
    this.tasks.push(task)
  }
  // 所有都变成resolved或者任何一个变成reject
  all(){ ... }
  // 只要有一个状态改变就结束
  race(){  ... }
  // 只有等到所有参数实例都返回结果,不管是fulfilled还是rejected,包装实例才会结束
  allSettled(){ ... }
  any(){ ... }
}

const timeout = (time,res) => new Promise(resolve => {
  setTimeout(()=>{resolve(res)}, time);
})

const rejectAsync = (time,res) => new Promise((resolve, reject) => {
  throw new Error('async error!')
  reject(new Error('test'));   //有了throw,此处不会执行
})

const notPromise = () => 'not Promise'

let promise = new PromiseCls();

all

all(){
   let promiseArr = this.tasks;
   return new Promise((resolve, reject)=>{
    let resArr = [];
    let index = 0;
    for(let i=0,len=promiseArr.length; i<len; i++){
      //如果不是Promise实例需要转化为Promise实例
      Promise.resolve(promiseArr[i]).then(res => {
      //promiseArr[i].then(res => {
        index++;
        // console.log(res) //not Promise, 3, 4, 2, 1
        resArr[i] = res; //i得对应上
        if( index === len){
          resolve(resArr);
        }
      }).catch(e => {
        console.log('one promise error:'+e)
        reject(e)
      }); 
    }
  }).catch((err)=> {
    throw new Error('all function:' + err) 
  })
}

promise.addAsync(timeout(600, '1'));
promise.addAsync(timeout(500, '2'));
promise.addAsync(timeout(300, '3'));
promise.addAsync(timeout(400, '4'));
promise.addAsync(notPromise());
promise.all().then((res)=>{console.log(res)})  
> 
[ '1', '2', '3', '4', 'not Promise' ]    按照顺序输出

promise.addAsync(timeout(600, '1'));
promise.addAsync(timeout(500, '2'));
promise.addAsync(timeout(300, '3'));
promise.addAsync(rejectAsync(400, '4'));
promise.all().then((res)=>{console.log(res)}).catch((err)=> {
  console.log('final catch error:',err)
})

>
one promise error:Error: async error!
final catch error: Error: all function:Error: async error!
     at .../Promise.js:28:13

promise.addAsync(timeout(600, '1'));
promise.addAsync(timeout(500, '2'));
promise.addAsync(timeout(300, '3'));
promise.addAsync(rejectAsync(400, '4'));
promise.addAsync(rejectAsync(200, '4'));
promise.all().then((res)=>{console.log(res)}).catch((err)=> {
  console.log('final catch error:',err)
})

>
one promise error:Error: async error!
one promise error:Error: async error!
final catch error: Error: all function:Error: async error!
    at .../Promise.js:28:13

race

// 只要有一个状态改变就结束,如果不是Promise实例需要转化为Promise实例
race(){
  let promiseArr = this.tasks;
  return new Promise((resolve, reject)=>{
    for(let i=0,len=promiseArr.length; i<len; i++){
      Promise.resolve(promiseArr[i]).then(res => {
     // promiseArr[i].then(res => {
        resolve(res) //一经改变就不再可变
      }).catch(e => {
        console.log('one promise error:'+e)
        reject(e)
      }); 
    }
  }).catch((err)=> {
    throw new Error('all function:' + err) 
  })
}
  
promise.addAsync(timeout(600, '1'));
promise.addAsync(timeout(500, '2'));
promise.addAsync(timeout(300, '3'));
promise.addAsync(timeout(400, '4'));

promise.race().then((res)=>{console.log(res)})  
> 3

promise.addAsync(timeout(600, '1'));
promise.addAsync(timeout(500, '2'));
promise.addAsync(timeout(300, '3'));
promise.addAsync(rejectAsync(400, '4'));
promise.addAsync(rejectAsync(200, '4'));
promise.race().then((res)=>{console.log(res)}).catch((err)=> {
  console.log('final catch error:',err)
})

>
one promise error:Error: async error!
one promise error:Error: async error!
final catch error: Error: all function:Error: async error!
    at .../Promise.js:28:13

allSettled

 // 不管是成功还是失败,都要全部执行完,所以需要考虑到finally
  // 无论请求对错最终都会返回一个数组对象 到 .then 中,并切返回的数据中标识了错误跟正确数据的区别
allSettled(){
  let promiseArr = this.tasks;
  return new Promise((resolve) => {
    let sttled = 0
    let resArr = []
    for(let i=0,len=promiseArr.length; i<len; i++){
      Promise.resolve(promiseArr[i])
      .then(res => { 
        resArr[i] = res; 
        // resArr[i] = {
          //   status: 'fulfilled',
          //   value: res
          // }
      })
      .catch(err => { 
        resArr[i] = err; 
        // resArr[i] = {
          //   status: 'rejected',
          //   reason: err
          // }  这么输出更清晰
      })
      .finally(() => { ++sttled === promiseArr.length && resolve(resArr) })
    }
  }) 
}


promise.addAsync(timeout(600, '1'));
promise.addAsync(timeout(500, '2'));
promise.addAsync(timeout(300, '3'));
promise.addAsync(notPromise());
promise.addAsync(rejectAsync());
promise.addAsync(rejectAsync());
promise.allSettled().then((res)=>{console.log(res)}).catch((err)=> {
  console.log('final catch error:',err)
})

>
[
  '1',
  '2',
  '3',
  'not Promise',
  Error: async error!
      at .../Promise.js:107:9
  Error: async error!
      at .../Promise.js:107:9
]

any

子Promise中任意一个Promise完成时,就触发.then,成功触发.then中第一个方法,失败触发.then中第二个方法

 // 只要一个成功就成功,失败要全部执行完
any(){
  let promiseArr = this.tasks;
  return new Promise((resolve, reject)=>{
    let index = 0
    let resArr = []
    for(let i=0,len=promiseArr.length; i<len; i++){
      Promise.resolve(promiseArr[i]).then(res => {
      // promiseArr[i].then(res => {
        resolve(res)
      }).catch(e => {
        index++ 
        resArr.push(e);
        if(index==promiseArr.length){
          reject(resArr)
          console.log('one promise error:'+e)
        }
      })
    }
  })
  // .catch((err)=> {
  //   throw new Error('all function:' + err) 
  // })
}
  
promise.addAsync(timeout(600, '1'));
promise.addAsync(timeout(500, '2'));
promise.addAsync(timeout(300, '3'));
promise.addAsync(notPromise());
promise.addAsync(rejectAsync());
promise.addAsync(rejectAsync());
promise.any().then((res)=>{console.log(res)}).catch((err)=> {
  console.log('final catch error:',err)
})

> not Promise


promise.addAsync(rejectAsync());
promise.addAsync(rejectAsync());
promise.any().then((res)=>{console.log(res)}).catch((err)=> {
  console.log('final catch error:',err)
})
>
[
  Error: async error!
      at .../Promise.js:107:9
  Error: async error!
      at .../Promise.js:107:9
]

Promise.resolve(4) Promise A+创建两次微任务参考: 【V8源码补充篇】从一道让我失眠的 Promise 面试题开始,深入分析 Promise 实现细节 - 掘金 (juejin.cn)

Promise.resolve().then(() => {
    console.log(0);
    return Promise.resolve(4);
}).then((res) => {
    console.log(res)
})

Promise.resolve().then(() => {
    console.log(1);
}).then(() => {
    console.log(2);
}).then(() => {
    console.log(3);
}).then(() => {
    console.log(5);
}).then(() =>{
    console.log(6);
})

// 0  1 2 3 4 5    因为执行了2次微任务,所以4在3后面

原生 Promise 创建两次微任务的位置

  • 第一次: 在发现 Promise.resolve(4) 的时候,创建 NewPromiseResolveThenableJob,并将其送入微任务队列
  • 第二次: 在处理 Promise.resolve(4) 的时候,调用 then 方法时,内部创建了微任务来处理回调函数