手撸promise,一步步实现,超详细

548 阅读8分钟

Promise基础相关

Promise 对象用于表示一个异步操作的最终完成 (或失败)及其结果值。Promise-Javascript | MDN

这里是promise的规范文档 Promise A+规范

promise在前端的工作学习以及面试中都是经常出现的知识,仅仅熟练使用他对我来说已经无法满足我的好奇心,有必要亲自实现它一下,开始吧!!!

手动实现

实现思路

  1. promise接受一个立即执行的函数,该函数有两个回调参数,resolve,reject
  2. promise有三个状态,pending, fulfilled, rejected, 且只能由pending转为其他两中状态
  3. proise的then方法,可以拿到变化后的promise的状态,并返回一个新的promise对象
  4. then方法中对回调值的处理函数的实现
  5. promise的静态方法,resolve,reject,race,all

新建TjfPromise类,传入回调参数并立即执行

// 定义promise的三种状态
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class TjfPromise{
    constructor(callback){
        this.status = PENDING // 初始态为pending
        this.value = null
        this.reason = null
        try{
            // promise中传入的函数是立即执行的,这里注意调用时候的this指向
            // promise中的resolve,reject函数调用的是内部实现的函数
            callback(this.resolve.bind(this), this.reject.bind(this))
        }catch(error){
            this.reject(error)
        }
    }
    // promise的状态只能由pending --> fulfilled 或者 pending --> rejected
    // 调用resolve方法的时候,status变为fulfilled
    resolve(value){
        if(this.status === PENDING){
            this.status = FULFILLED
            this.value = value
        }
    }
    // 调用reject方法的时候,status变为rejected
    reject(reason){
        if(this.status === PENDING){
            this.status = REJECTED
            this.reason = reason
        }
    }
}

初步实现then方法

class TjfPromise {
    ......
    then(onFulfilled, onRejected){
        switch(this.status){
            case FULFILLED:
                onFulfilled(this.value)
                break
            case REJECTED:
                onRejected(this.reason)
                break
        }
    }
}

拿两个小例子测试一下目前的代码

new TjfPromise((resolve, reject) => {
    resolve(11)
}).then(value => console.log(value))

结果打印出来11,说明目前初步实现。

但是这里有三个问题,第一个问题是,then方法里面只对fulfilled和rejected两种状态做了判断,pending的时候,也就是promise中的代码没有立即执行,也就是异步的时候没有处理,毕竟promise主要就是来处理异步任务的。

第二个问题是多次调用then方法的时候(非链式调用),后面的then调用的时候,前面的then的内容可能还没有执行,需要存储起来

第三个问题是then函数是支持链式调用的,promise A+ 规范里面说then必须返回一个promise对象

image.png 我们先解决第一个问题,对异步调用的支持

then方法对异步的支持

class TjfPromise{
    constructor(callback){
        this.status = PENDING
        this.value = null
        this._status = PENDING // 新增变量,配合status的set,get
        this.reason = null
        // 新增两个变量用来存储pending时的回调函数
        this.FULFILLED_CALLBACK = null
        this.REJECTED_CALLBACK = null
        try{
            callback(this.resolve.bind(this), this.reject.bind(this))
        }catch(error){
            this.reject(error)
        }
    }
    get status(){ // 新增get
        return this._status
    }
    set status(newStatus){ // 新增set
        this._status = newStatus // 用_status防止嵌套触发set,陷入死循环
        if(newStatus === FULFILLED){ // 状态改变的时候立即调用存储的回调
            this.FULFILLED_CALLBACK(this.value)
        }
        if(newStatus === REJECTED){
            this.REJECTED_CALLBACK(this.reason)
        }
    }
    resolve(value){
        if(this.status === PENDING){
            this.value = value // 赋值要在状态改变的前面,顺序不能变
            this.status = FULFILLED
        }
    }
    reject(reason){
        if(this.status === PENDING){
            this.reason = reason
            this.status = REJECTED            
        }
    }
    then(onFulfilled, onRejected){
        switch(this.status){
            case FULFILLED:
                onFulfilled(this.value)
                break
            case REJECTED:
                onRejected(this.reason)
                break
            case PENDING// pending的时候先将回调函数存储起来,等待调用
                this.FULFILLED_CALLBACK = onFulfilled
                this.REJECTED_CALLBACK = onRejected
        }
    }
}

我们来测试一下现在的代码

new TjfPromise((resolve, reject) => {
  setTimeout(() => {
    resolve(11)
  }, 1000)
}).then((value) => console.log(value))

延时了一秒之后打印了11,说明没有问题。下面解决第二个问题,then的多次调用问题

then方法对多次调用的支持

我们需要对刚刚的代码进行一些更改,把回调放在一个数组里面存储即可

class TjfPromise{
    constructor(){
        // 把刚刚用来存储pending时的回调函数的变量改造为两个数组
        this.FULFILLED_CALLBACK_LIST = []
        this.REJECTED_CALLBACK_LIST = []
    }
    set status(newStatus){ // setter 中做一些更改
        this._status = newStatus
        if(newStatus === FULFILLED){ // 依次执行所有保存的回调
            this.FULFILLED_CALLBACK_LIST.forEach( callback => callback(this.value))
        }
        if(newStatus === REJECTED){
            this.REJECTED_CALLBACK_LIST.forEach( callback => callback(this.reason))
        }       
    }
    then(onFulfilled, onRejected){ // 对then方法的改造
        switch(this.status){
            case FULFILLED:
                onFulfilled(this.value)
                break
            case REJECTED:
                onRejected(this.reason)
                break
            case PENDING// pending的时候先将回调函数存储起来,等待调用
                this.FULFILLED_CALLBACK_LIST.push(onFulfilled)
                this.REJECTED_CALLBACK_LIST.push(onRejected)
    }
}

测试一下

const promise = new TjfPromise((resolve, reject) => {
  setTimeout(() => {
    resolve(11)
  }, 1000)
})
promise.then( value => console.log(1,value))
promise.then( value => console.log(2,value))
promise.then( value => console.log(3,value))

打印结果

1 11
2 11
3 11

没有问题,第二个问题解决了,下面就是最麻烦的第三个问题,then函数的返回

then方法的完善

这里的实现按照promise A+文档的要求来实现,比较繁琐

then方法中对回调参数的判断

then方法如果传入的参数不是或者没有传值,应该做处理为一个直接返回的函数

class TjfPromise{
    ......
    then(onFulfiled, onRejected){
        // 对传入的参数进行处理
        const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : value => value
        const realOnFulfilled = this.isFunction(onRejected) ? onRejected : reason => {throw reason}
    }
    isFunction(func){ // 新增验证是否为函数
        return typeof func === 'function'
    }
}

then方法应该返回一个promise

class TjfPromise{
    ......
    then(onFulfilled, onRejected){
        const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : value => value
        const realOnRejected = this.isFunction(onRejected) ? onRejected : reason => {throw reason}
        const promise2 = new TjfPromise((resolve, reject) => {
            switch(this.status){
                case FULFILLED:
                    realOnFulfilled(this.value)
                    break
                case REJECTED:
                    realOnRejected(this.reason)
                    break
                case PENDING// pending的时候先将回调函数存储起来,等待调用
                    this.FULFILLED_CALLBACK_LIST.push(realOnFulfilled)
                    this.REJECTED_CALLBACK_LIST.push(realOnRejected)
            }
        })
        return promise2
    }
}

这里需要对realOnfulfilled的值做判断,这里我们开始实现resolvePromise函数

then方法里面加入resolvePromise函数,先对realOnFulfilled处理

class TjfPromise{
    ......
    then(onFulfilled, onRejected){
        const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : value => value
        const realOnRejected = this.isFunction(onRejected) ? onRejected : reason => {throw reason}
        const promise2 = new TjfPromise((resolve, reject) => {
            try{
                const x = realOnFulfilled(this.value)
                this.resolvePromise(promise2, x, resolve, reject) // 新增resolvePromise函数
            }catch(error){
                reject(error)
            }
            switch(this.status){
                case FULFILLED:
                    realOnFulfilled(this.value)
                    break
                case REJECTED:
                    realOnRejected(this.reason)
                    break
                case PENDING:
                    this.FULFILLED_CALLBACK_LIST.push(realOnFulfilled)
                    this.REJECTED_CALLBACK_LIST.push(realOnRejected)
            }
        })
        return promise2
    }
}

开始实现resolvePromise函数

class TjfPromise{
    ......
    resolvePromise(promise2, x, resolve, reject){
            // 返回值就是promise2的时候,要直接返回错误
        if( x === promise2) return new TypeError('The promise and the x refer to the same')
        // x是Tjfpromise对象的时候, 要调用它的then方法
        if( x instanceof TjfPromise){
            x.then(resolve, reject)
        } else {
            resolve(x)
        }
    }
}

这里有一个问题,在这里promise2是还没有完成初始化的,因此会报错,那么我们需要把resolve的调用放在queueMicrotask微任务里面执行,同时把realOnRejected也一块处理一下

queueMicrotask里面执行resolvePromise

class TjfPromise{
    ......
    then(onFulfilled, onRejected){
        const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : value => value
        const realOnRejected = this.isFunction(onRejected) ? onRejected : reason => {throw reason}
        const promise2 = new TjfPromise((resolve, reject) => {
            const fulfilledMicrotask = () => {
                try{
                    queueMicrotask(() => { // 微任务里面调用
                        const x = realOnFulfilled(this.value)
                        this.resolvePromise(promise2, x, resolve, reject)
                    })
                }catch(error){
                    reject(error)
                }
            }
            const rejectedMicrotask = (() => {
                try{
                    queueMicrotask(() => { // 微任务里面调用
                        const x = realOnRejected(this.reason)
                        this.resolvePromise(promise2, x, resolve, reject)
                    })
                }catch(error){
                    reject(error)
                }
            })
            switch(this.status){
                case FULFILLED:
                    fulfilledMicrotask()
                    break
                case REJECTED:
                    rejectedMicrotask()
                    break
                case PENDING:
                    this.FULFILLED_CALLBACK_LIST.push(fulfilledMicrotask)
                    this.REJECTED_CALLBACK_LIST.push(rejectedMicrotask)
            }
        })
        return promise2
    }
}

至此then函数的内容基本完结,下面按照promise A+规范把resolvePromise的内容完善一下

完善resolvePromise方法

class TjfPromise{
    ......
    resolvePromise(promise2, x, resolve, reject){
         if( x === promise2) return new TypeError('The promise and the x refer to the same')
         if( x instanceof TjfPromise){
            queueMicrotask(() => { // 这里又加了一层微任务,更加符合promise A+的调用
                x.then( y => {
                    this.resolvePromise(promise2, y, resolve, reject)
                }, reject)
            })
         } else if(typeof x === 'object' || this.isFunction(x)){
            if(x === null) resolve(x)
            let then = null
            try{
                then = x.then
            } catch(error){
                return reject(error)
            }
            if(this.isFunction(x.then)) {
                let called = false
                try{
                  then.call(x,(y) => {
                    if(called) return
                    called = true
                    this.resolvePromise(promise2, y, resolve, reject)
                  }, (error) => {
                    if(called) return
                    called = false
                    reject(error)
                  })
                }catch(error){
                  if(called) return
                  reject(error)
                }
              } else {
                resolve(x)
              }
            }else {
            resolve(x)
        }
    }
}

到这里,resolvePromise方法也告一段落,测试一下

const promise = new TjfPromise((resolve, reject) => {
  resolve('success')
})

function other () {
  return new TjfPromise((resolve, reject) =>{
    resolve('other')
  })
}
promise.then(value => {
  console.log(1)
  console.log('resolve', value)
  return other()
}).then(value => {
  console.log(2)
  console.log('resolve', value)
})

打印结果

1
resolve success
2
resolve other

没问题

静态方法的实现

promise内部有一些静态方法,就是只能通过prmise.race()调用,而不是实例调用,我们实现其中常用的几个

resolve方法

静态resolve方法就是返回了一个promise对象

class TjfPromise{
    ......
    static resolve(value){
        if(value instanceof TjfPromise){
            return value
        }
        return new TjfPromise((resolve, reject) => {
            resolve(value)
        })
    }
}

reject方法

静态reject方法返回一个带有reason的promise对象

class TjfPromise{
    ......
    static reject(reason){
        return new TjfPromise((resolve, reject) => {
            reject(reason)
        })
    }
}

race方法

Promise.race(iterable) 方法返回一个 promise,一旦迭代器中的某个promise解决或拒绝,返回的 promise就会解决或拒绝。

class TjfPromise{
    static race(promiselist){
        return new TjfPromise((resolve, reject) => {
            if(!promiselist.length) return resolve()
            promiselist.forEach( promiseitem => TjfPromise.resolve(promiseitem).then( value => resolve(value), reason => reject(reason)))
            })
    }
}

all 方法

promise.all(iterable)方法,返回一个promise实例,value的值为一个数组,如果有一个为rejected那么返回rejected

class TjfPromise{
    ......
    static all(promiselist) {
        return new TjfPromise((resolve, reject) => {
          let valuelist = [];
          let count = 0
          if (!promiselist.length) return resolve();
          promiselist.forEach((promiseitem, index) => {
            TjfPromise.resolve(promiseitem).then(
              (value) => {
                valuelist[index] = value
                count++
                if (count === promiselist.length) resolve(valuelist)
              },
              (reason) => reject(reason)
            );
          });
      });
  }
}

测试一下

const promise1 = new TjfPromise((resolve, reject) => {
  setTimeout(() => {
    resolve(22)
  },1000)
})
const promise2 = new TjfPromise((resolve, reject) => {
  resolve(11)
})

TjfPromise.all([promise1, promise2]).then( value => console.log(value), reason => console.log(reason))

打印结果

[22, 11]

没有问题,那么以上就是prmise的全部实现了,附上全部完整代码

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

class TjfPromise {
  constructor(callback) {
    this.status = PENDING;
    this.value = null;
    this._status = PENDING;
    this.reason = null;
    this.FULFILLED_CALLBACK_LIST = [];
    this.REJECTED_CALLBACK_LIST = [];
    try {
      callback(this.resolve.bind(this), this.reject.bind(this));
    } catch (error) {
      this.reject(error);
    }
  }
  get status() {
    return this._status;
  }
  set status(newStatus) {
    this._status = newStatus
    if (newStatus === FULFILLED) {
      this.FULFILLED_CALLBACK_LIST.forEach((callback) => callback(this.value));
    }
    if (newStatus === REJECTED) {
      this.REJECTED_CALLBACK_LIST.forEach((callback) => callback(this.reason));
    }
  }
  resolve(value) {
    if (this.status === PENDING) {
      this.value = value;
      this.status = FULFILLED;
    }
  }
  reject(reason) {
    if (this.status === PENDING) {
      this.reason = reason;
      this.status = REJECTED;
    }
  }
  then(onFulfilled, onRejected) {
    const realOnFulfilled = this.isFunction(onFulfilled)
      ? onFulfilled
      : (value) => value;
    const realOnRejected = this.isFunction(onRejected)
      ? onRejected
      : (reason) => {throw reason};
    const promise2 = new TjfPromise((resolve, reject) => {
      const fulfilledMicrotask = () => {
        try {
          queueMicrotask(() => {
            const x = realOnFulfilled(this.value);
            this.resolvePromise(promise2, x, resolve, reject);
          });
        } catch (error) {
          reject(error);
        }
      };
      const rejectedMicrotask = () => {
        try {
          queueMicrotask(() => {
            const x = realOnRejected(this.reason);
            this.resolvePromise(promise2, x, resolve, reject);
          });
        } catch (error) {
          reject(error);
        }
      };
      switch (this.status) {
        case FULFILLED:
          fulfilledMicrotask();
          break;
        case REJECTED:
          rejectedMicrotask();
          break;
        case PENDING:
          this.FULFILLED_CALLBACK_LIST.push(fulfilledMicrotask);
          this.REJECTED_CALLBACK_LIST.push(rejectedMicrotask);
      }
    });
    return promise2;
  }
  resolvePromise(promise2, x, resolve, reject) {
    if (x === promise2)
      return new TypeError("The promise and the x refer to the same");
    if (x instanceof TjfPromise) {
      queueMicrotask(() => {
        x.then((y) => {
          this.resolvePromise(promise2, y, resolve, reject);
        }, reject);
      });
    } else if (typeof x === "object" || this.isFunction(x)) {
      if (x === null) resolve(x);
      let then = null;
      try {
        then = x.then;
      } catch (error) {
        return reject(error);
      }
      if (this.isFunction(x.then)) {
        let called = false;
        try {
          then.call(
            x,
            (y) => {
              if (called) return;
              called = true;
              this.resolvePromise(promise2, y, resolve, reject);
            },
            (error) => {
              if (called) return;
              called = false;
              reject(error);
            }
          );
        } catch (error) {
          if (called) return;
          reject(error);
        }
      } else {
        resolve(x);
      }
    } else {
      resolve(x);
    }
  }
  static resolve(value) {
    if (value instanceof TjfPromise) {
      return value;
    }
    return new TjfPromise((resolve, reject) => {
      resolve(value);
    });
  }
  static reject(reason) {
    return new TjfPromise((resolve, reject) => {
      reject(reason);
    });
  }
  static race(promiselist) {
    return new TjfPromise((resolve, reject) => {
      if (!promiselist.length) return resolve();
      promiselist.forEach((promiseitem) =>
        TjfPromise.resolve(promiseitem).then(
          (value) => resolve(value),
          (reason) => reject(reason)
        )
      );
    });
  }
  static all(promiselist) {
    return new TjfPromise((resolve, reject) => {
      let valuelist = [];
      let count = 0
      if (!promiselist.length) return resolve();
      promiselist.forEach((promiseitem, index) => {
        TjfPromise.resolve(promiseitem).then(
          (value) => {
            valuelist[index] = value
            count++
            if (count === promiselist.length) resolve(valuelist)
          },
          (reason) => reject(reason)
        );
      });
    });
  }
  isFunction(func) {
    return typeof func === "function";
  }
}

参考文章:

从一道让我失眠的Promise面试题开始,深入实现Promise实现细节