像素

Promise 实现

基本结构:

class _Promise {
 constructor(executor) {
   this.state = "PENDING"; // 定义 Promise 状态
   this.value = undefined; // 存储 promise 对应状态的值
    
   this.fulfilledCallbacks = [];  // 存储成功的回调
   this.rejectedCallbacks = [];  // 存储失败的回调
   const resolve = () => {};
   const reject = () => {};
   executor(resolve,reject);
 }
  then(){}
  catch(){}
}

其中 resolvedCallbacks、rejectedCallbacks 分别处理 同一个 promise 实例注册多个 then 时对应执行的回调。

比如这种情况,promise 当 promise 实例 Fulfilled 时,所有注册的成功回调都要执行,Rejected 同理

let promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(1);
  }, 1000);
});
// 注册的第一个 then
promise.then((value) => {
  console.log(value); // 输出 1
  return value + 1;
});
// 注册的第二个 then
promise.then((value) => {
  console.log(value); // 输出 1
  return value * 2;
});

实现 resolve、reject

const resolve = (val) => {
  // 保证回调函数执行顺序
  setTimeout(() => {
    this.value = val;
    this.status = "FULFILLED";
    // 执行当前实例上注册过的 所有回调
    this.fulfilledCallbacks.forEach(f => f(this.value))
  }, 0)
};

const reject = (reason) => {
  setTimeout(() => {
    this.value = reason;
    this.status = "REJECTED";
    this.rejectedCallbacks.forEach(f => f(this.value))
  }, 0)
};

其中使用 resolve 和 reject setTimeout 也可以不使用,Promise A+ 没有要求,只是优化策略。

实现 then

  1. 判断三种情况分别进行处理(PENDING|FULFILLED|REJECTED)
  2. 如果参数不是函数,要设置默认参数逻辑
  3. 返回一个新的 Promise 保证链式调用可行
  4. 要考虑 then 的回调函数返回值是 promise 的情况

当状态是 FULFILLED|REJECTED 时,说明 Promise 已经执行完成,按照规范异步执行onRejected,onFulfilled

PENDING 时 其实就是分别把 FULFILLED 和 REJECTED 的异步处理放进 对应 Callbacks 中等待状态更新后执行。

class _Promise {
  // ...略
  then (onFulfilled, onRejected) {
    const _this = this;
    // 默认值很重要 为了链式调用
    // 如果 onFulfilled 不是函数,则其被忽略,同时向后传递异步结果 value。
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val;

    // 如果 onRejected 不是函数,则其被忽略,直接抛出异常,交给后面的处理程序来处理。
    onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };

    if (_this.status === "REJECTED") {
      return new _Promise((resolve, reject) => {
        setTimeout(() => {
          const x = onRejected(_this.value)
          // 如果是 promise 得到其结果
          if (x instanceof Promise) {
            x.then(resolve, reject);
          } else {
            resolve(x);
          }
        }, 0)
      });
    } else if (_this.status === "FULFILLED") {
      return new _Promise((resolve, reject) => {
        setTimeout(() => {
          const x = onFulfilled(_this.value)
          // 如果是 promise 得到其结果
          if (x instanceof Promise) {
            x.then(resolve, reject);
          } else {
            resolve(x);
          }
        }, 0)
      });
    } else { // 处理 pending 状态
      // 此时 onFulfilled 和 onRejected 外层不需要包裹 setTimeout 模拟异步执行
      // 因为在 resolve 和 reject 执行他们的时候 
      // 已经是使用 setTimeout 进行异步执行了
      const P = new _Promise((resolve, reject) => {
        // 添加 resolve Callbacks
        _this.fulfilledCallbacks.push(() => {
            const x = onFulfilled(_this.value)
            // 如果是 promise 得到其结果
            if (x instanceof Promise) {
              x.then(resolve, reject);
            } else {
              resolve(x);
            }
        });
        // 添加 reject Callbacks
        _this.rejectedCallbacks.push(() => {
          const x = onRejected(_this.value)
          // 如果是 promise 得到其结果
          if (x instanceof Promise) {
            x.then(resolve, reject);
          } else {
            resolve(x);
          }
        });
      })
      return P;

    }
  };
}

其中在 pending 状态下,将 then 中注册的回调添加进 Callbacks 数组时通过 包裹一层 setTimeout,将回调的执行添加进宏任务队列,这样可以保证在异步的情况下也能够获得 this.value

promise 规范中是在添加进微任务队列中等待执行。

添加处理循环引用报错判断

当一个 Promise 对象在它的 then 方法中返回自身时,确实会形成一个无限循环调用,导致 **"Chaining cycle detected for promise" **

循环引用示例:

let promise = new Promise((resolve, reject) => {
    resolve(1000);
})

let p1 = promise.then(value => {
    console.log(value);
    return p1;
})

p1.then(resolve => {
    console.log(resolve); // 不会执行
}).catch(error => {
    console.log(error.message); // 输出 "Chaining cycle detected for promise"
})

添加判断,抽取成 resolvePromise 方法

  1. 判断回调函数的返回值x是否为Promise对象
  2. 如果x是Promise对象,则必须等待其状态改变后,新的Promise才能被resolve/reject。
  3. 如果x为普通值(非Promise),则直接将新Promise状态改为resolved,并将x作为该Promise对象的值传递下去。
if (this.status === "FULFILLED") {
  return new _Promise((resolve, reject) => {
    setTimeout(() => {
      const x = onFulfilled(_this.value)
      this.resolvePromise(_this, res, resolve, reject);
    }, 0)
  });
}


// 调用then或catch后返回的新 Promise 对象的内部判断
_Promise.prototype.resolvePromise = function (promise, x, resolve, reject) {
  if (promise === x) { // 循环引用了
    return reject(new TypeError("Chaining cycle detected for promise"));
  }
  // 如果异步结果不是 Promise 类型,则直接解析为成功状态。
  if (x instanceof Promise) {
    x.then(resolve, reject);
  } else {
    resolve(x);
  }
}

实现 catch

catch 相当于 then(null, onRejected);的简写形式。接受一个回调函数作为参数,该回调函数会接收到 Promise 抛出的错误信息,可以利用这个错误信息来进行相应的处理。此外,catch 方法返回一个新的 Promise 对象,并将该对象的状态标记为 resolved。

catch(onRejected) {
  // 只处理异常情况,等同于 then(null, onRejected)
  this.then(null, onRejected);
}

添加错误处理

完整实现代码:

class _Promise {
  constructor(executor) {
    this.status = "PENDING";
    this.value = undefined;

    /**
     * 存储成功和失败的回调函数
     * 用于处理 多个promise.then() 或 多个 promise.catch() 的情况
     * 比如
     * promise.then()
     * promise.then()
     * promise.catch()
     * promise.catch()
     */
    this.fulfilledCallbacks = [];
    this.rejectedCallbacks = [];

    const resolve = (val) => {
      // 用于处理异步情况,宏任务永远能拿到 异步(微任务)传入的val
      setTimeout(() => {
        this.value = val;
        this.status = "FULFILLED";
        this.fulfilledCallbacks.forEach((f) => f(this.value));
      });
    };

    const reject = (reason) => {
      setTimeout(() => {
        this.value = reason;
        this.status = "REJECTED";
        this.rejectedCallbacks.forEach((f) => f(this.value));
      });
    };

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

  /**
   * then 实现
   * @param onFulfilled
   * @param onRejected
   * @returns {_Promise}
   */
  then(onFulfilled, onRejected) {
    const _this = this;
    // 默认值很重要 为了链式调用
    // 如果 onFulfilled 不是函数,则其被忽略,同时向后传递异步结果 value。
    onFulfilled =
      typeof onFulfilled === "function" ? onFulfilled : v => v;

    // 如果 onRejected 不是函数,则其被忽略,直接抛出异常,交给后面的处理程序来处理。
    onRejected =
      typeof onRejected === "function" ? onRejected : e => { throw e };

    if (_this.status === "FULFILLED") {
      const P = new _Promise((resolve, reject) => {
        // 按照规范 onFulfilled 异步执行
        setTimeout(() => {
          try {
            const res = onFulfilled(_this.value);
            this.resolvePromise(P, res, resolve, reject);
          } catch (e) {
            reject(e); // 错误处理 改变 promise 状态为 REJECTED
          }
        });
      });
      return P;
    } else if (_this.status === "REJECTED") {
      const P = new _Promise((resolve, reject) => {
        // 按照规范 onRejected 异步执行
        setTimeout(() => {
          try {
            const res = onRejected(_this.value);
            _this.resolvePromise(P, res, resolve, reject);
          } catch (e) {
            reject(e);
          }
        });
      });
      return P;
    } else {       // 处理 pending 状态
      const P = new _Promise((resolve, reject) => {
        _this.fulfilledCallbacks.push(() => {
          try {
            let res = onFulfilled(_this.value);
            _this.resolvePromise(P, res, resolve, reject);
          } catch (r) {
            reject(r);
          }
        });
        _this.rejectedCallbacks.push(() => {
          try {
            let res = onRejected(_this.value);
            _this.resolvePromise(P, res, resolve, reject);
          } catch (r) {
            reject(r);
          }
        });
      })
      return P;
    }
  }

  /**
   * catch实现
   * @param onRejected
   */
  catch(onRejected) {
    // 只处理异常情况,等同于 then(null, onRejected)
    this.then(null, onRejected);
  }
}

/**
 * 调用then或catch后返回的新 Promise 对象的内部判断
 * @param promise
 * @param x
 * @param resolve
 * @param reject
 * @returns {*}
 */
_Promise.prototype.resolvePromise = function (promise, x, resolve, reject) {
  if (promise === x) {
    return reject(new TypeError("Chaining cycle detected for promise"));
  }
  // 如果异步结果不是 Promise 类型,则直接解析为成功状态。
  if (x instanceof _Promise) {
    x.then(resolve, reject);
  } else {
    resolve(x);
  }
}

Promise.resolve

1.传参为一个 Promise, 则直接返回它。

2.传参为一个 thenable 对象,返回的 Promise 会跟随这个对象,采用它的最终状态作为自己的状态。

3.其他情况,直接返回以该值为成功状态的promise对象。

Promise._resolve = function (p) {
  if (p instanceof Promise) return p;
  return new Promise((resolve, reject) => {
    if (p && p.then && typeof p.then === "function") {
      p.then(resolve, reject);
    } else {
      resolve(p);
    }
  });
};

Promise.reject

Promise.reject 中传入的参数会作为一个 reason 原封不动地往下传

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

Promise.finally

  • finally 里的函数,无论如何都会执行,并会把前面的值原封不动传递给下一个then方法中 (重点)
  • 如果 finally 函数中有 promise 等异步任务,会等它们全部执行完毕,再结合之前的成功与否状态,返回值
Promise._finally = function (callback) {
  return this.then(
    (data) => {
      return Promise.resolve(callback()).then(() => data);
    },
    (err) => {
      return Promise.resolve(callback()).then(() => {
        throw err;
      });
    }
  );
};

Promise.all

传入参数为一个空的可迭代对象,则直接进行resolve。

如果参数中有一个promise失败,那么Promise.all返回的promise对象失败。

在任何情况下,Promise.all 返回的 promise 的完成状态的结果都是一个数组


Promise._all = function (promises) {
  let res = [];
  let count = 0; // 计数器 重点 判断所有promise执行完成的依据
  const len = promises.length;
  return new Promise((resolve, reject) => {
    if (len === 0) return resolve(res);
    promises.forEach((p,i) => {
      // 不能保证传入的每一项都为 promise 所以包一层
      Promise.resolve(p).then(data => {
        res[i] = data;
        count++;
        if (count === len) resolve(res);
      }).catch(e => {
        reject(e);
      })
    })
  });
};

Promise.allSettled

Promise.allSettled 跟 Promise.all 类似, 其参数接受一个Promise的数组, 返回一个新的Promise,

唯一的不同在于, 其不会进行短路, 也就是说当Promise全部处理完成后我们可以拿到每个Promise的状态, 而不管其是否处理成功。

Promise._allSettled = function (promises) {
  let res = [];
  let count = 0; // 技术器 重点 判断所有promise执行完成的依据
  const len = promises.length;
  return new Promise((resolve, reject) => {
    if (len === 0) return resolve(res);
    promises.forEach((p,i) => {
      // 不能保证传入的每一项都为 promise 所以包一层
      Promise.resolve(p).then(data => {
        res[i] = {
          status: 'fulfilled',
          value: data,
        };
        count++;
        if (count === len) resolve(res);
      }).catch(e => {
        res[i] = {
          status: 'rejected',
          reason: e,
        };
        count++;
        if (count === len) resolve(res);
      })
    })
  });
};

Promise.race

/**
 * race 只与首个 settled 的promise有关,遍历顺序可以忽略不计
 * @param promises
 * @returns {Promise<unknown>}
 * @private
 */
Promise._race = function (promises) {
  return new Promise((resolve, reject) => {
    promises.forEach(p => {
      Promise.resolve(p).then(data => {
        resolve(data);
      }).catch(e => reject(e));
    });
    // 处理空数组情况
    if (promises.length === 0) {
      resolve();
    }
  });
}