🌂

JS Promise

已经很长时间没有写 JS 了,现在写一个 JS Promise 的简单实现。

版本1

class SimplePromise {
  constructor(executor) {
    this.status = 'pending';
    this.value = undefined;
    this.reason = undefined;
    const resolve = (value) => {
      if (this.status === 'pending') {
        this.status = 'fulfilled';
        this.value = value;
      }
    };
    const reject = (reason) => {
      if (this.status === 'pending') {
        this.status = 'rejected';
        this.reason = reason;
      }
    };
    try {
      executor(resolve, reject);
    } catch (err) {
      reject(err);
    }
  }
  then(onFulfilled, onRejected) {
    if (this.status === 'fulfilled') {
      onFulfilled(this.value);
    }
    if (this.status === 'rejected') {
      onRejected(this.reason);
    }
  }
}

这个版本大概能看明白 Promise 需要的参数,以及它的执行时机。但是有一个问题,当调用 then 方法传入的函数时候,如果异步某一执行完成,此时还在 pending 状态,那这个函数不会再被执行。所以需要优化一下,将需要当状态改变后执行的函数存起来,并且 Promise 可以无限被 then 所以要存到数组中。

版本2

class SimplePromise {
  constructor(executor) {
    this.status = 'pending';
    this.value = undefined;
    this.reason = undefined;
    this.onFulfilledCallbacks = [];
    this.onRejectedCallbacks = [];
    const resolve = (value) => {
      if (this.status === 'pending') {
        this.status = 'fulfilled';
        this.value = value;
        this.onFulfilledCallbacks.forEach(cb => cb(this.value));
      }
    };
    const reject = (reason) => {
      if (this.status === 'pending') {
        this.status = 'rejected';
        this.reason = reason;
        this.onRejectedCallbacks.forEach(cb => cb(this.reason));
      }
    };
    try {
      executor(resolve, reject);
    } catch (err) {
      reject(err);
    }
  }
  then(onFulfilled, onRejected) {
    if (this.status === 'fulfilled') {
      onFulfilled(this.value);
    } else if (this.status === 'rejected') {
      onRejected(this.reason);
    } else if (this.status === 'pending') {
      this.onFulfilledCallbacks.push(onFulfilled);
      this.onRejectedCallbacks.push(onRejected);
    }
  }
}

在这个版本中,已经可以简单运行并执行测试一下:

const p = new SimplePromise(resolve => setTimeout(resolve, 1000, Math.random()))
setTimeout(() => {
  p.then(v => console.log(`inner: ${v}`))
}, 3000)
p.then(v => console.log(`outer: ${v}`))

最终的执行是这样的:

1秒后打印:outer: 0.5220189262043281
再过2秒打印:inner: 0.5220189262043281

但它仍然欠缺一点东西,如果 resolve 函数的返回值仍然是一个 Promise, 那它应该继续自动执行这个 Promise;如果不是 Promise 而是一个准确的值,那需要保证是异步。所以有了版本3

版本3

class SimplePromise {
  constructor(executor) {
    this.status = 'pending';
    this.value = undefined;
    this.reason = undefined;
    this.onFulfilledCallbacks = [];
    this.onRejectedCallbacks = [];

    const resolve = (value) => {
      if (value instanceof SimplePromise) {
        return value.then(resolve, reject);
      }
      setTimeout(() => {
        if (this.status === 'pending') {
          this.status = 'fulfilled';
          this.value = value;
          this.onFulfilledCallbacks.forEach(cb => cb(this.value));
        }
      }, 0);
    };
    const reject = (reason) => {
      setTimeout(() => {
        if (this.status === 'pending') {
          this.status = 'rejected';
          this.reason = reason;
          this.onRejectedCallbacks.forEach(cb => cb(this.reason));
        }
      }, 0);
    };
    try {
      executor(resolve, reject);
    } catch (err) {
      reject(err);
    }
  }
  then(onFulfilled, onRejected) {
    if (this.status === 'fulfilled') {
      setTimeout(() => onFulfilled(this.value), 0);
    } else if (this.status === 'rejected') {
      setTimeout(() => onRejected(this.reason), 0);
    } else if (this.status === 'pending') {
      this.onFulfilledCallbacks.push(onFulfilled);
      this.onRejectedCallbacks.push(onRejected);
    }
  }
}

当然真正的 Promise 还是要继续优化,比如链式调用的 then 和错误处理,以及 Promise.all 等方法。

这个链接里面有另外一种实现方式,大同小异。


文章不是你的,当你读了十遍,那便是你的了。