2

这篇文章通过自己实现一个Promise来加深对promise的理解,具体如何实现符合promise规范的代码,可以查看Promises/A+

梳理 Promise 功能

1、Promise是一个构造函数,它接收一个执行函数作为参数,执行函数里面包含resolvereject两个方法;

2、调用resolve方法表示成功,reject方法表示失败,结果会在实例的then函数中以参数函数的方式拿到;

3、then是实例方法,参数包含成功的回调函数和失败的回调函数,只用resolvereject执行后方法才会执行;

状态变化

promise里面的状态决定了当前能执行什么方法,先定义三个变量来保存状态:

// 等待,用户还没调用resolve或者reject
const PENDING_STATUS = "pending";
// 成功,用户调用了resolve
const FULFILLED_STATUS = "fulfilled";
// 失败,用户调用了reject
const REJECTED_STATUS = "rejected";

有状态就会有出现状态的原因了,后面还需要把这些原因传给then方法,我们先创建构造函数:

function Promise(executor) {
  this.status = PENDING_STATUS;
  // 保存resolve方法的参数
  this.value = null; 
  // 保存reject方法的参数
  this.reason = null; 
}

定义好了状态我们还要知道什么时候去修改它,我们知道executor执行函数接受resolvereject两个方法,这两个方法执行的时候就是我们修改状态的时候了:

function Promise(executor) {
    this.status = PENDING_STATUS;
    // 保存resolve方法的参数
    this.value = null;
    // 保存reject方法的参数
    this.reason = null; 

  const resolve = (value) => {
    // 如果状态不是 pending 返回
    if (this.status !== PENDING_STATUS) return;
    // 先保存参数
    this.value = value;
    // 改变状态
    this.status = FULFILLED_STATUS;
  };

  const reject = (reason) => {
    if (this.status !== PENDING_STATUS) return;
    this.reason = reason;
    this.status = REJECTED_STATUS;
  };

  try {
    executor(resolve, reject);
  } catch (error) {
    reject(error);
  }
}

then 方法

我们知道then方法接受两个参数,成功后的函数回调和失败后的函数回调,那么我们可以在函数里面判断当前状态,并执行对应的方法:

Promise.prototype.then = function (onfulfilled, onrejected) {
+  if (this.status === FULFILLED_STATUS) {}
+  if (this.status === REJECTED_STATUS) {}
+  if (this.status === PENDING_STATUS) {}
};

根据状态去判断执行onfulfilled还是onrejected方法比较容易,但是如果状态仍处于 pending,就不能直接执行onfulfilledonrejected了,我们可以利用发布订阅的方式将方法缓存起来,直到resolvereject被调用才取出来执行,我们先在构造函数里面定义onResolvedCallbackonRejectedCallback两个数组:

function Promise(executor) {
+ this.onResolvedCallback = [];
+ this.onRejectedCallback = [];
}

then方法里面,我们在pending状态时先将执行函数保存起来:

Promise.prototype.then = function (onfulfilled, onrejected) {
  if (this.status === FULFILLED_STATUS) {
    onfulfilled(this.value);
  }

  if (this.status === REJECTED_STATUS) {
    onrejected(this.reason);
  }

  if (this.status === PENDING_STATUS) {
    this.onResolvedCallback.push(() => {
      onfulfilled(this.value);
    });

    this.onRejectedCallback.push(() => {
      onrejected(this.reason);
    });
  }
};

resolvereject函数中取出并执行:

const resolve = (value) => {
  if (this.status !== PENDING_STATUS) return;
  this.value = value;
  this.status = FULFILLED_STATUS;
  // 取出缓存的方法并执行
+  this.onResolvedCallback.forEach((fn) => fn());
};

链式调用

promise方法可以实现链式调用,上一个函数onfulfilled的返回结果,可以在下一次then函数中被获取:

new Promise((resolve) => resolve("result1"))
  .then((r) => `result2`)
  .then((r) => `result3`)
  .then((r) => `result4`);

可以看到then方法可以不断的被链式调用,这说明then方法也返回了一个promise实例:

Promise.prototype.then = function (onfulfilled, onrejected) {
  let promise = new Promise((resolve, reject) => {
    if (this.status === FULFILLED_STATUS) {
      onfulfilled(this.value);
    }

    if (this.status === REJECTED_STATUS) {
      onrejected(this.reason);
    }

    if (this.status === PENDING_STATUS) {
      this.onResolvedCallback.push(() => {
        onfulfilled(this.value);
      });

      this.onRejectedCallback.push(() => {
        onrejected(this.reason);
      });
    }
  });

  return promise;
};

但是onfulfilledonrejected的返回结果,有可能是普通的值或者是promise,所以我们也要处理下返回的值:

let promise = new Promise((resolve, reject) => {
  if (this.status === FULFILLED_STATUS) {
    let value = onfulfilled(this.value);
    // 函数在setTimeout里面执行,这样能保证拿到返回的promise变量
    setTimeout(() => {
      try {
        resolvePromise(promise, value, resolve, reject);
      } catch (error) {
        onrejected(error);
      }
    });
  }

  if (this.status === REJECTED_STATUS) {
    let value = onrejected(this.reason);
    setTimeout(() => resolvePromise(promise, value, resolve, reject));
  }

  if (this.status === PENDING_STATUS) {
    this.onResolvedCallback.push(() => {
      let value = onfulfilled(this.value);
      setTimeout(() => {
        try {
          resolvePromise(promise, value, resolve, reject);
        } catch (error) {
          onrejected(error);
        }
      });
    });

    this.onRejectedCallback.push(() => {
      let value = onrejected(this.reason);
      setTimeout(() => resolvePromise(promise, value, resolve, reject));
    });
  }
});

resolvePromise函数我们要做的处理是判断返回的value值,这里有几个判断需要注意:

  • value 跟返回的 promise 相同,循环引用直接抛出错误
  • valuePromise 实例,在 then 方法里面执行 resolve
  • 直接resolve value
function resolvePromise(promise, value, resolve, reject) {
  if (promise === value) throw new Error("循环引用");

  if (value instanceof Promise) {
    let then = value.then;

    try {
      then.call(
        value,
        (result) => {
          resolve(result);
        },
        (error) => {
          reject(error);
        }
      );
    } catch (error) {
      reject(error);
    }
  } else {
    resolve(value);
  }
}

值的穿透

Promise支持一种比较奇葩的写法:

let p = new Promise((resolve) => resolve("result"));

p.then()
  .then()
  .then((result) => console.log(result));

遇到这种写法,需要在then函数里面提前做判断:

Promise.prototype.then = function(onfulfilled,onrejected){
+    onfulfilled = typeof onfulfilled === "function" ? onfulfilled : (val)=>val;
+    onrejected = typeof onrejected === "function" ? onrejected : (error)=>error;
}

catch

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

resolve 和 reject

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

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

Promise.all

all方法接受一个数组,我们需要判断数组里面的Promise方法,只有在then方法执行后才算完成,可以定义一个完成变量保存完成的数量,当完成数量等于数组长度时,才resolve最终的结果

Promise.all = function (values) {
  return new Promise((resolve, reject) => {
    let result = [];
    // 执行完成的数量
    let resultIndex = 0;
    let len = values.length;

    function processData(index, value) {
      result[index] = value;
      // 执行完成后加一
      resultIndex++;
      if (resultIndex === len) {
        // 最终结果
        resolve(result);
      }
    }

    for (let i = 0; i < len; i++) {
      let value = values[i];
      let then = value.then;
      if (then && typeof then === "function") {
        then.call(
          value,
          (result) => {
            processData(i, result);
          },
          (reject) => {
            processData(i, reject);
          }
        );
      } else {
        processData(i, value);
      }
    }
  });
};

race

race执行返回最快完成的结果,所以当then函数执行后直接执行resolve返回结果:

Promise.race = function (values) {
  return new Promise((resolve, reject) => {
    for (let i = 0; i < values.length; i++) {
      let value = values[i];
      let then = value.then;

      if (then && typeof then === "function") {
        then.call(
          value,
          (result) => {
            resolve(result);
          },
          reject
        );
      } else {
        resolve(value);
        break;
      }
    }
  });
};

chenwl
117 声望5 粉丝

平坦的路面上曲折前行