jsernews 1.0.1

<~>

ts 128 days ago.

原文链接:https://blog.safia.rocks/post/170154422915/how-do-promises-work-under-the-hood

所以,我知道我说过我想从这些代码中稍稍休息一下,但是好奇心得到了最好的我。

我最近正在做一个找工作的现场面试。是的,我还没有找到工作,而且我在短短几周内即将毕业。我试图不去想(或恐慌)它。不管怎样,在面试的其中一个阶段,我的任务是实现 JavaScript Promise 对象的内部部分。在我完成面试后,我觉得我真的想知道 Promises 内部是如何工作的。

所以我要去研究它!

在我们开始之前,如果您懂一点 Promise 的含义,可能会有所帮助。如果你不熟悉,你可以查看这个快速解释MDN 上的 Promise 文档

针对这种情况,我决定查看 JavaScript 中最流行的 Promises 实现

所以这个特定项目的代码库比 Node 的代码库小很多,这对我来说是个好消息!核心逻辑存储在 src/core.js 源文件中。在这个文件中,定义了 Promise 对象。

所以,一个 promise 始于传递一个函数给构造函数。在构造函数中,有一些内部变量被初始化,然后调用 doResolve 函数。

function Promise(fn) {
  if (typeof this !== 'object') {
    throw new TypeError('Promises must be constructed via new');
  }
  if (typeof fn !== 'function') {
    throw new TypeError('Promise constructor\'s argument is not a function');
  }
  this._deferredState = 0;
  this._state = 0;
  this._value = null;
  this._deferreds = null;
  if (fn === noop) return;
  doResolve(fn, this);
}

doResolve 函数接受传递给 Promise 构造函数的函数以及对当前 Promise 的引用。所以我跳到了 doResolve 函数的定义,并试图找出那里发生了什么。所以看起来它会调用另一个叫做 tryCallTwo 的函数,它接受两个回调函数。当某个值成功返回时执行一个回调,而在出现错误时执行另一个回调。如果回调成功执行,则使用 Promise 对象和值调用 resolve 函数,否则调用 reject 函数。

function doResolve(fn, promise) {
  var done = false;
  var res = tryCallTwo(fn, function (value) {
    if (done) return;
    done = true;
    resolve(promise, value);
  }, function (reason) {
    if (done) return;
    done = true;
    reject(promise, reason);
  });
  if (!done && res === IS_ERROR) {
    done = true;
    reject(promise, LAST_ERROR);
  }
}

所以接下来我想要做的就是更好地了解 tryCallTwo 在做什么。事实证明它非常简单。基本上,它是一个轻量包装函数,它调用接受的参数中的第一个参数(一个函数),并传递接下来的两个参数。

function tryCallTwo(fn, a, b) {
  try {
    fn(a, b);
  } catch (ex) {
    LAST_ERROR = ex;
    return IS_ERROR;
  }
}

所以本质上,这一切是我们调用用户在创建 Promise 对象时传递的函数。类似于这个样子。

new Promise((resolve, reject) => {
  // some code goes here
});

它调用上面定义的回调函数。然后,回调函数中会继续调用在此文件中全局定义的 resolvereject 函数。我决定检查在这个情况下 resolve 会做什么。

这个函数从数据检查开始。您尝试解析的值不能是您试图解析的 Promise 对象本身。

function resolve(self, newValue) {
  // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
  if (newValue === self) {
    return reject(
      self,
      new TypeError('A promise cannot be resolved with itself.')
    );
  }

然后函数检查 newValue 是否是一个对象或函数。如果是,它会尝试使用 getThen helper 函数获取在其上定义的 then 函数。

if (
  newValue &&
  (typeof newValue === 'object' || typeof newValue === 'function')
) {
  var then = getThen(newValue);
  if (then === IS_ERROR) {
    return reject(self, LAST_ERROR);
  }

此时,函数执行另一次检查以查看 newValue 是否为一个 promise。这是一个必要的检查,针对你在 then 中返回一个 promise 的情况,因为你可能会链式调用多个 then 方法。它也做了一些工作来设置早期初始化的内部变量。

if (
  then === self.then &&
  newValue instanceof Promise
) {
  self._state = 3;
  self._value = newValue;
  finale(self);
  return;

最后,它试图用已经返回的新值再次解析 then 函数​​。

else if (typeof then === 'function') {
  doResolve(then.bind(newValue), self);
  return;
}

实际上,我很高兴地看到 Promise 对象的代码在很多方面与我在面试中实现的类似。这是一种解脱!

我发现它处理链式 then 的方式非常有趣。那实际上是我在面试中被卡住的事情之一,并且看到在这个 Promise 实现中使用的方法的简单性让我感到在智力上感到满意。

我的好奇心已经满足了!我希望你喜欢这篇文章!