/ 前端

从零制作一个Node.js的Promise

Step 1

首先,什么是Promise?
ES6 spec定义里面,Promise是一个类,其constructor接收一个executor函数。其实例都会有then方法。当然,在spec里面,Promises还有更详细的其他的属性,这里为了简单起见,暂且忽略不表。

首先,一个比较简陋的实现:

class iPromise {
    constructor(executor) {
    }

    then(onFulfilled, onRejected) {}
}

Step 2

其次,Promise是一个有三种状态的状态机:

  • pending: 初始状态,promises任务结果尚未确定
  • fulfilled: 任务成功,并产生了相关数据结果
  • rejected: 任务失败,并产生了相关错误信息
    根据这些要求,丰富一下构造函数:
const PROMISE_STATE = {
    PENDING: 'PENDING',
    FULFILLED: 'FULFILLED',
    REJECTED: 'REJECTED'
}
constructor(executor) {
  if (typeof executor !== 'function') {
    throw new Error('Executor must be a function');
  }

  // Internal state.
  // `$chained` is an array of the functions we need to call once this promise is settled.
  this.$state = PROMISE_STATE.PENDING;
  this.$chained = [];

  const resolve = res => {
    if (this.$state !== PROMISE_STATE.PENDING) {
      return;
    }

    this.$state = PROMISE_STATE.FULFILLED;
    this.$internalValue = res;

    for (const { onFulfilled } of this.$chained) {
      onFulfilled(res);
    }
  };
  const reject = err => {
    if (this.$state !== PROMISE_STATE.PENDING) {
      return;
    }
    this.$state = PROMISE_STATE.REJECTED;
    this.$internalValue = err;
    for (const { onRejected } of this.$chained) {
      onRejected(err);
    }
  };

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

然后是then函数,其功能也不复杂:如果当前是FULFILLED状态,则调用onFulfilled;如果当前是REJECTED状态,则调用onRejected;最后,如果PENDING状态,说明正忙,把当前任务push到$chained中,等待后续执行。
实现如下:

then (onFulFilled, onRejected) {
    if (this.$state === PROMISE_STATE.FULFILLED) {
        onFulfilled(this.$internalValue);
    } else if (this.$state === PROMISE_STATE.REJECTED) {
        onRejected(this.$internalValue);
    } else {
        this.$chained.push({ onFulfilled, onRejected });
    }
}

Step 3

Promise里面比较复杂的部分在于Chaining部分。
也就是说,then函数返回的应该也是个promise,保证经手的promise状态一致性,以此来保证Promise的状态可以在一连串的调用过程之后,依然保持连贯。
所以,then的实现改成这样:

then(onFulfilled, onRejected) {
  return new iPromise((resolve, reject) => {
    const _onFulfilled = res => {
      try {
        resolve(onFulfilled(res));
      } catch (err) {
        reject(err);
      }
    };
    const _onRejected = err => {
      try {
        reject(onRejected(err));
      } catch (_err) {
        reject(_err);
      }
    };
    if (this.$state === PROMISE_STATE.FULFILLED) {
      _onFulfilled(this.$internalValue);
    } else if (this.$state === PROMISE_STATE.REJECTED) {
      _onRejected(this.$internalValue);
    } else {
      this.$chained.push({ onFulfilled: _onFulfilled, onRejected: _onRejected });
    }
  });
}

还没完,你可能注意到,如果onFulfilled返回的是promise,那么resolve是不是也应该处理这种情况呢?所以,需要将resolve重构如下:

const resolve = res => {
  if (this.$state !== 'PENDING') {
    return;
  }

  const then = res != null ? res.then : null;
  if (typeof then === 'function') {
    return then(resolve, reject);
  }

  this.$state = 'FULFILLED';
  this.$internalValue = res;

  for (const { onFulfilled } of this.$chained) {
    onFulfilled(res);
  }

  return res;
};

如果你能理解这段代码,那么你应该更加清楚resolved promise 和 fulfilled promise的区别了,仔细体会一下。

另外,Chaining里面其他的细节,例如Error propagation等尚需完善。

其他需要补充的细节

  • catch()
  • Async/await
从零制作一个Node.js的Promise
Share this