手写Promise

142 阅读4分钟

讲promise文章很多,本文主要是学习手写promise源码记录,实测跑过了所有PromiseA+用例,希望对大家有所帮助。

代码即文档

// 先定义三个常量表示状态
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
    constructor(executor){
        this.status = PENDING; // 储存状态的变量,初始值是 pending
        this.value = null; // 成功之后的值
        this.reason = null; // 失败之后的原因
        this.onFulfilledCallbacks = []; // 存储成功回调函数
        this.onRejectedCallbacks = []; // 存储失败回调函数
        // resolve和reject为什么要用箭头函数?
        // 如果直接调用的话,普通函数this指向的是window或者undefined
        // 用箭头函数就可以让this指向当前实例对象
        // 更改成功后的状态
        let resolve = (value) => {
            // 只有状态是等待,才执行状态修改
            if (this.status === PENDING) {           
                this.status = FULFILLED; // 状态修改为成功 
                this.value = value;  // 保存成功之后的值
                // resolve里面将所有成功的回调拿出来执行
                this.onFulfilledCallbacks.forEach(fn => fn());
            }
        }
        
        // 更改失败后的状态
        let reject = (reason) => {
            // 只有状态是等待,才执行状态修改
            if (this.status === PENDING) {
                this.status = REJECTED; // 状态成功为失败 
                this.reason = reason; // 保存失败后的原因
                // reject里面将所有失败的回调拿出来执行
                this.onRejectedCallbacks.forEach(fn => fn());      
            }
        }
        // executor 是一个执行器,进入会立即执行
        // 并传入resolve和reject方法
        try {
          executor(resolve, reject)
        } catch (error) {
          this.reject(error)
        }
      }

    then(onFulfilled, onRejected) {
        // 如果不传,就使用默认函数
        const realOnFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
        const realOnRejected = typeof onRejected === 'function' ? onRejected : reason => {throw reason};


        let promise2 = new MyPromise((resolve, reject) => {
            const fulfilledMicrotask = () =>  {
                // 创建一个微任务等待 promise2 完成初始化
                queueMicrotask(() => {
                    try {
                        // 获取成功回调函数的执行结果
                        const x = realOnFulfilled(this.value);
                        // 传入 resolvePromise 集中处理
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (error) {
                        reject(error)
                    } 
                })  
            }
    
            const rejectedMicrotask = () => { 
                // 创建一个微任务等待 promise2 完成初始化
                queueMicrotask(() => {
                    try {
                        // 调用失败回调,并且把原因返回
                        const x = realOnRejected(this.reason);
                        // 传入 resolvePromise 集中处理
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (error) {
                        reject(error)
                    } 
                }) 
            }
            // 判断状态
            if (this.status === FULFILLED) {
                fulfilledMicrotask() 
            } else if (this.status === REJECTED) { 
                rejectedMicrotask()
            } else if (this.status === PENDING) {
                // 等待
                // 因为不知道后面状态的变化情况,所以将成功回调和失败回调存储起来
                // 等到执行成功失败函数的时候再传递
                this.onFulfilledCallbacks.push(fulfilledMicrotask);
                this.onRejectedCallbacks.push(rejectedMicrotask);
            }

        })
        return promise2;
    }

    catch (onRejected) {
        // 只需要进行错误处理
        this.then(undefined, onRejected);
    }

    finally (fn) {
        return this.then((value) => {
            return MyPromise.resolve(fn()).then(() => {
            return value;
        });
    }, (error) => {
        return MyPromise.resolve(fn()).then(() => {
        throw error
        });
    });
    }

    static resolve(parameter) {
        // 如果传入是promise,则直接返回
        if (parameter instanceof MyPromise) {
            return parameter;
        }
        return new MyPromise((resolve) => {
            resolve(parameter)
        })
    }

    static reject(parameter) {
        if (parameter instanceof MyPromise) {
            return parameter;
        }
        return new Promise((resolve, reject) => {
            reject(parameter)
        })
    }

    static all(promiseList) {
        return new MyPromise((resolve, reject) => {
            const result = [];
            const length = promiseList.length;
            let count = 0;

            if (length === 0) {
                return resolve(result);
            }

            promiseList.forEach((promise, index) => {
                MyPromise.resolve(promise).then((value) => {
                    count++;
                    result[index] = value;
                    if (count === length) {
                        resolve(result);
                    }
                }, (reason) => {
                    reject(reason);
                })
            })
        })
    }

    static allSettled(promiseList) {
        return new MyPromise((resolve, reject) => {
            const result = [];
            const length = promiseList.length;
            const count = 0;

            if (length === 0) {
                return resolve();
            }

            promiseList.forEach((promise, index) => {
                MyPromise.resolve(promise).then((value) => {
                    count++;
                    result[index] = {
                        status: FULFILLED,
                        value: value
                    }
                    if (count === length) {
                        return resolve(result);
                    }
                }, (reason) => {
                    count++;
                    result[index] = {
                        status: REJECTED,
                        reason: reason
                    }
                    if (count === length) {
                        return resolve(result);
                    }
                })

            })
        })
    }


    static race(promiseList) {
        return new MyPromise((resolve, reject) => {
            const length = promiseList.length;
            if (length === 0) {
                return resolve();
            }
            for (let i = 0; i < length; i++) {
                MyPromise.resolve(promiseList[i]).then(value => {
                    return resolve(value);
                }, (reason) => {
                    return reject(reason);
                });
            }
        })
    }
}

function resolvePromise(promise, x, resolve, reject) {
    // 如果 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise
  // 这是为了防止死循环
  if (promise === x) {
    return reject(new TypeError('The promise and the return value are the same'));
  }

  if (typeof x === 'object' || typeof x === 'function') {
    // 这个坑是跑测试的时候发现的,如果x是null,应该直接resolve
    if (x === null) {
      return resolve(x);
    }

    let then;
    try {
      // 把 x.then 赋值给 then 
      then = x.then;
    } catch (error) {
      // 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise
      return reject(error);
    }

    // 如果 then 是函数
    if (typeof then === 'function') {
      let called = false;
      // 将 x 作为函数的作用域 this 调用之
      // 传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise
      // 名字重名了,我直接用匿名函数了
      try {
        then.call(
          x,
          // 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
          y => {
            // 如果 resolvePromise 和 rejectPromise 均被调用,
            // 或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
            // 实现这条需要前面加一个变量called
            if (called) return;
            called = true;
            resolvePromise(promise, y, resolve, reject);
          },
          // 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
          r => {
            if (called) return;
            called = true;
            reject(r);
          });
      } catch (error) {
        // 如果调用 then 方法抛出了异常 e:
        // 如果 resolvePromise 或 rejectPromise 已经被调用,则忽略之
        if (called) return;

        // 否则以 e 为据因拒绝 promise
        reject(error);
      }
    } else {
      // 如果 then 不是函数,以 x 为参数执行 promise
      resolve(x);
    }
  } else {
    // 如果 x 不为对象或者函数,以 x 为参数执行 promise
    resolve(x);
  }
  };

  MyPromise.deferred = function () {
    var result = {};
    result.promise = new MyPromise(function (resolve, reject) {
      result.resolve = resolve;
      result.reject = reject;
    });
  
    return result;
  }
  module.exports = MyPromise;

遇到的问题

在实现then方法时 ,为了实现promise链式调用,对then方法结果返回进行promise封装,代码如下:

image.png 但是在执行时报如下错误:


(node:10540) UnhandledPromiseRejectionWarning: ReferenceError: Cannot access 'promise2' before initialization

解决方法:从错误提示可以看出,我们必须等待 promise2完成初始化,再此我们可以通过setTimeout实现异步,但是promise A+规范中使用微任务实现,所以采用queueMicrotask

image.png