手写promise

139 阅读5分钟

先理解Promise做了哪些事情和原理

new Promise说明Promise是个构造函数

① 必填参数:executor是一个函数,不是函数的话,会报错的

② 返回一个promise实例对象

executor 是立即执行的函数,它接收两个形参也是函数:resolve、reject

promise实例的状态和值

① 默认状态:pending,默认值undefined

② 变为成功态:执行resolve函数时,改变状态为:fulfilled,值:函数的参数

③ 变为失败态:

=> 执行reject函数时,改变状态为:rejected,值:函数的参数

=> executor函数执行时,报错了,改变状态为:rejected,值:报错的原因【不影响其他代码执行】

promise实例是说一不二的主,状态只改变一次

每一个promise实例上有一个then方法,接收两个形参也是函数:onFulfilled、onRejected

① then方法是同步执行的,它的参数现在只是注入,等着promise实例有结果了再执行

② onFulfilled 接收成功态的结果

③ onRejected 接收失败的结果

.then(onFulfilled, onRejected)

① 如果此时promise实例已经是成功或者失败,创建一个异步微任务(JS自己写不了微任务,这里咱用定时器代替),等待同步任务结束,执行对应的函数即可

② 此时状态还是pending,我们需要把onFulfilled、onRejected保存起来,当后期状态修改了(比如:resovle/reject方法执行),再次通知保存的方法,而这个操作也是异步微任务

Promise的then用法

1、 then方法创建并返回了一个新的promise实例

2、 then方法的参数:onFulfilled、onRejected 不是函数,会穿透延顺到下一个then方法中去

3、 失败态的promise延顺到最后,会被catch接收到

Promise对象上的方法

(1)Promise.resolve

Promise.resolve = function (value){
     return new Promise(function (resolve){
         resolve(value);
     });
 };

(2)Promise.reject

Promise.reject = function (value){
     return new Promise(function (_, reject){
         reject(value);
     });
 };

(3)Promise.all

Promise.all的使用:

1、接收一个数组为参数,返回一个promise实例

2、数组中,有一个失败,则结果就是失败了

3、数组所有的内容都成功了,按照原来的顺序排列结果

① 数组里面的内容是promise的话,要拿到它的结果才行

② 数组里面的内容不是promise的话,收集起来

③ 收集到所有的结果以后,就让返回的promise实例成功

Promise.all = function (promises) {
     // 返回了一个promise实例对象
     return new Promise(function (resolve, reject) {
         try {
             var index = 0,
                 len = promises.length,
                 results = [];
 
             for (var i = 0; i < len; i++) {
                 (function (i) { // 自执行匿名函数,保证i的正确性
                     var item = promises[i];
                     if (!isPromise(item)) { // 不是Promise
                         index++;
                         results[i] = item;
                         index === len ? resolve(results) : null;
                         return;
                     }
                     // 是Promsie调用它的then方法
                     item.then(function (result) {
                         index++;
                         results[i] = result;
                         index === len ? resolve(results) : null;
                     }, function (reason) {
                         reject(reason);
                     });
                 })(i);
             }
         } catch (err) {
             reject(err);
         }
     });
 };

然后将上面的内容翻译成代码:

思路:首先想想我们平时使用的 promise

先定义三个常量表示状态

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

新建 MyPromise 类(这样就直接可以在内部写then()、all()等)

ES5中创建类的方法:新建一个构造函数,定义一个方法并且赋值给构造函数的原型

ES6实现类非常简单,只需要类声明,和ES5中使用构造函数不同的是,在ES6中,我们将原型的实现写在了类中,但本质上还是一样的,都是需要新建一个类名,然后实现构造函数,再实现原型方法。私有属性是实例中的属性,不会出现在原型上,且只能在类的构造函数或方法中创建。

// 新建 MyPromise 类
class MyPromise {
  constructor(executor){
    // executor 是一个执行器,进入会立即执行
    // 传入resolve和reject方法
    try {
      executor(this.resolve, this.reject)
    } catch (error) {
      this.reject(error)
    }
  }

  // 储存状态的变量,初始值是 pending
  status = PENDING;
  // 成功之后的值
  value = null;
  // 失败之后的原因
  reason = null;

  // 存储成功回调函数
  onFulfilledCallbacks = [];
  // 存储失败回调函数
  onRejectedCallbacks = [];
  
  // resolve和reject为什么要用箭头函数?
  // 如果直接调用的话,普通函数this指向的是window或者undefined
  // 用箭头函数就可以让this指向当前实例对象
  // 也可以 let _this = this

  // 更改成功后的状态
  resolve = (value) => {
    // 只有状态是等待,才执行状态修改
    if (this.status === PENDING) {
      // 状态修改为成功
      this.status = FULFILLED;
      // 保存成功之后的值
      this.value = value;
      // resolve里面将所有成功的回调拿出来执行
      while (this.onFulfilledCallbacks.length) {
        // Array.shift() 取出数组第一个元素,然后()调用,shift不是纯函数,取出后,数组将失去该元素,直到数组为空
        this.onFulfilledCallbacks.shift()(value)
      }
    }
  }

  // 更改失败后的状态
  reject = (reason) => {
    // 只有状态是等待,才执行状态修改
    if (this.status === PENDING) {
      // 状态成功为失败
      this.status = REJECTED;
      // 保存失败后的原因
      this.reason = reason;
      // resolve里面将所有失败的回调拿出来执行
      while (this.onRejectedCallbacks.length) {
        this.onRejectedCallbacks.shift()(reason)
      }
    }
  }

  then(onFulfilled, onRejected) {
    const realOnFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
    const realOnRejected = typeof onRejected === 'function' ? onRejected : reason => {throw reason};

    // 为了链式调用这里直接创建一个 MyPromise,并在后面 return 出去
    const 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
      });
    });
  }

  // resolve 静态方法
  static resolve (parameter) {
    // 如果传入 MyPromise 就直接返回
    if (parameter instanceof MyPromise) {
      return parameter;
    }

    // 转成常规方式
    return new MyPromise(resolve =>  {
      resolve(parameter);
    });
  }

  // reject 静态方法
  static reject (reason) {
    return new MyPromise((resolve, reject) => {
      reject(reason);
    });
  }

  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) => {
      const length = promiseList.length;
      const result = [];
      let count = 0;

      if (length === 0) {
        return resolve(result);
      } else {
        for (let i = 0; i < length; i++) {
            const currentPromise = MyPromise.resolve(promiseList[i]);
            currentPromise.then((value) => {
              count++;
              result[i] = {
                status: 'fulfilled',
                value: value
              }
              if (count === length) {
                return resolve(result);
              }
            }, (reason) => {
              count++;
              result[i] = {
                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();
      } else {
        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