原稿:语雀 · Promise · 原目录:手撕代码 › Promise

参考链接:

Promise 实现

类构造器

  • promise 有三类状态、解决值、拒绝理由,具有私有实例方法 resolve、reject
  • 当 promise 处于 pending 态时,由 then() 添加的 onResolved/onRejected 回调需要在队列中暂存,等到期约状态落定时再去执行,因此需要两个保存回调的数组
  • promise 构造时传入的 executor 会立刻去执行

基于这些基本要求,可以写出下方 promise 的类构造器

const PENDING = "PENDING";
const RESOLVED = "RESOLVED";
const REJECTED = "REJECTED";

class Promise {
  constructor(executor) {
    this.status = PENDING;
    this.value = undefined;
    this.reason = undefined;
    this.onResolvedCallbacks = [];
    this.onRejectedCallbacks = [];

    let resolve = (value) => {
      if (this.status === PENDING) {
        this.status = RESOLVED;
        this.value = value;
        this.onResolvedCallbacks.forEach((fn) => fn());
      }
    };

    let reject = (reason) => {
      if (this.status === PENDING) {
        this.status = REJECTED;
        this.reason = reason;
        this.onRejectedCallbacks.forEach((fn) => fn());
      }
    };

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

then() & resolvePromise()

then 是 promise 类上的静态方法,接收两个回调 onResolved/onRejected,会在 promise 落定为 resolved 或 rejected 时执行,当 promise 未落定时,回调会先放入队列中

  • 返回 promise:标准要求回调返回一个 promise,即 promise2,它包装自 onResolved/onRejected 的执行结果
  • 当 onResolved/onRejected 的执行结果 x 是一个 promise(thenable) ,递归地调用 x.then(),直到取得最后的值后再包装为 promise2 的 value
  • 当 x 是一个普通值,直接包装为 promise2 的 value
  • 当中间抛出任何错误,直接包装为 promise2 的 reason
  • 链式调用:then() 可以进行链式调用,前一个 then() 返回的 promise value/reason 会成为下一个 then() 中回调接收到的参数
  • 值穿透:当未显式提供回调时,会直接传递上一个 promise value 或 reason

基于此可以写出 then() 与 resolvePromise(),其中在 resolvePromise 中自始至终传递的始终都是 promise2 的 resolve 与 reject,

// 包装 onResolved/onRjected 返回值 x
// x是普通值:直接返回
// x是thenable对象:递归的调用它的then()
const resolvePromise = (promise2, x, resolve, reject) => {
  if (promise2 === x) {
    return reject(new TypeError("Chaining cycle detected for promise!"));
  }

  let called;
  // 当x是个thenable对象
  if ((typeof x === "object" && x != null) || typeof x === "function") {
    try {
      let then = x.then;
      if (typeof then === "function") {
        then.call(
          x,
          (y) => {
            if (called) return;
            called = true;
            resolvePromise(promise2, y, resolve, reject);
          },
          (r) => {
            if (called) return;
            called = true;
            reject(r);
          }
        );
        // x是个普通值
      } else {
        resolve(x);
      }
    } catch (error) {
      if (called) return;
      called = true;
      reject(error);
    }
    // x是个普通值
  } else {
    resolve(x);
  }
};

const PENDING = "PENDING";
const RESOLVED = "RESOLVED";
const REJECTED = "REJECTED";
class Promise {
  constructor(executor) { /* */ }

  // 静态方法
  then(onResolved, onRjected) {
    // 值穿透
    onResolved = typeof onResolved === "function" ? onResolved : (v) => v;
    onRejected =
      typeof onRjected === "function"
        ? onRjected
        : (error) => {
            throw error;
          };

    let promise2 = new Promise((resolve, reject) => {
      // 箭头函数this在定义时确定,因此这里this即调用then()的promise1
      if (this.status === RESOLVED) {
        setTimeout(() => {
          try {
            let x = onResolved(this.value);
            resolvePromise(promise2, x, resolve, reject);
          } catch (error) {

Promise.all()

参考链接:promise详解(promise.all实现、promise.race实现)

说明

Promise.all() 接收一个可迭代对象,返回一个新期约:该期约会在一组期约全部解决之后再解决

  • 合成的期约只会在每个包含的期约都解决之后才解决
  • 如果至少有一个包含的期约待定,则合成的期约也会待定
  • 如果有一个包含的期约拒绝,则合成的期约也会拒绝

返回值

返回一个新期约

  • 如果所有期约都成功解决,则合成期约的解决值就是所有包含期约解决值的数组
  • 参数中的非promise值将被忽略,但会被放在返回数组中(如果 promise 完成的话)
  • 数组元素将会按照参数内的 promise顺序排列,而不是由调用promise的完成顺序决定

实现

Promise.all = (promises) => {  return new Promise((resolve, reject) => {    const result = [];    let cnt = 0;    for (let i = 0; i < promises.length; i++) {      // 包装      Promise.resolve(promises[i]).then((value) => {        cnt++;        result[i] = value;        if (cnt == promises.length) resolve(result);      }, reject);    }  });};// 测试var p1 = Promise.resolve(3);var p2 = 1337;var p3 = new Promise((resolve, reject) => {  setTimeout(resolve, 100, "foo");});Promise.all([p1, p2, p3]).then((values) => {  console.log(values); // [3, 1337, "foo"]});

Promise.race()

说明

Promise.race() 接收一个可迭代对象,返回一个包装期约,是一组集合中最先解决/拒绝的期约的镜像

  • 无论是解决还是拒绝,只要是第一个落定的期约,Promise.race()就会包装其解决值或拒绝理由并返回新期约
  • 如果传的迭代是空的,则返回的 promise 将永远等待

返回值

返回一个新期约

  • 新期约是传入迭代中最先解决/拒绝的期约镜像
  • 传入迭代中的非promise值会被resolve()包装

实现

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

promise 限制并发数

class Scheduler {
  constructor(max) {
    this._max = max;
    this._count = 0;
    this._taskQueue = [];
  }

  add(caller, ...args) {
    return new Promise((resolve, reject) => {
      let task = this.createTask(caller, args, resolve, reject);
      if (this._count >= this._max) this._taskQueue.push(task);
      else task();
    });
  }

  createTask(caller, args, resolve, reject) {
    return () => {
      caller(...args)
        .then(resolve, reject)
        .finally(() => {
          this._count--;
          if (this._taskQueue.length) {
            let task = this._taskQueue.shift();
            task();
          }
        });
      this._count++;
    };
  }
}

// sleep()
const timeout = (time) =>
  new Promise((resolve) => {
    setTimeout(resolve, time);
  });

const scheduler = new Scheduler(2);
// const addTask = (time, order) => {
//   scheduler.add(() => timeout(time))
//           .then(() => console.log(order));
// };

async function addTask(time, order) {
  await scheduler.add(() => timeout(time));
  console.log(order);
}

addTask(1000, "1");
addTask(500, "2");
addTask(300, "3");
addTask(400, "4");
// 2 3 1 4