【问题标题】:In which order these code statements are moved to call stack?这些代码语句按什么顺序移动到调用堆栈?
【发布时间】:2019-08-09 09:56:32
【问题描述】:

我是 Javascript 新手,想了解 JS 的执行引擎。我知道任何异步代码语句都会移动到调用堆栈,然后立即被删除并在单独的 Web API 线程(由浏览器启动)中执行。然后将结果存储在回调队列中,一旦调用堆栈为空,事件循环就会拾取这些值。

谁能确认下面提到的语句将移动到调用堆栈的顺序?

Promise.resolve(function1)
  .then(function2)
  .then(function3)
  .then(function4);

console.log("Hello");

整个承诺链是移动到调用堆栈还是单独的thens ?

【问题讨论】:

  • executed in a separate Web API thread 不,Javascript 是单线程的。
  • Promise.resolved 应该是Promise.resolve
  • 在调用任何个回调函数之前,您会看到“Hello”。请注意,它是 .resolve,但它采用的值不是(通常)一个函数。

标签: javascript promise es6-promise javascript-engine


【解决方案1】:

整个承诺链是移动到调用堆栈还是单独的thens?

不是整个链条。还要小心你在这里表达的方式。个人thens 同步执行。它是异步执行的then 的回调。我想你指的是那些回调。

链接的then 方法在不同的promise 对象上执行,每个方法都会在Promise Job Queue 中排队一个reaction 处理程序, 但仅在相应的承诺解决时

所以我们这里至少有 4 个同步创建的 Promise 对象。对Promise.resolve 的调用会创建第一个, 立即解决。所有三个then 方法也都被执行,创建了 3 个待处理的 Promise。 让我们将这 4 个 promise 对象称为 A、B、C 和 D。所以 A 已解决,其他待处理。

当 promise A 被解决时,一个条目被放入 Promise Job Queue。我们称它为 H(A)(“对已解决的承诺 A 做出反应的处理程序”)

这一切都是同步发生的。同步脚本最终执行console.log后,调用栈为空,处理Promise Job Queue:

Promise 作业队列有 H(A)。它从该队列中拉出并放入调用堆栈。 这会触发第一个then 回调,即function2。 它的返回值用于解析promise B。 我们应该在这里考虑function2 返回一个promise E(可能是一个thenable)的情况,但我们首先假设它只是返回一个non-thenable。 Promise B 使用该值解析,并将新条目 H(B) 放入 Promise Job Queue。

调用栈又为空了。

再次处理 Promise 作业队列,现在有 H(B),...,所以它继续。

如果function2function3function4 返回 non-thenables,这将全部发生在一个任务中,但作业队列不会有 H(A)、H(B)、H(C),同时H(D)。 在这种情况下,队列一次只有一个。

更现实的情况是function2 之类的函数返回对异步 API 的调用结果,例如fetch

在这种情况下,承诺 B 将依赖于 function2 返回的承诺 E。此依赖项包括对 E 的 then 方法的异步调用。 无需过多详细介绍该过程,本质是 Promise 作业队列可能不会立即获得条目 H(E)。 这只会在 Promise E 解决时发生。当它这样做时,调用堆栈将从作业队列中获取 H(E)。 由于依赖关系,它的执行将涉及对 H(B) 的调用。

因为这个延迟,当前的Task会在某个时刻发现Job Queue是空的,任务就结束了。

然后事件循环将检测作业队列上何时有某些东西(例如 H(E) )。

其他一些说明

在单独的 Web API 线程中执行

没有用于执行此类异步代码的单独 Web API 线程。所有这一切都发生在一个线程中。

话虽如此,涉及的 API 可能包含非 JS 部分,例如访问 OS 函数,这些函数可能通过其他(非 JS)线程执行。还有
Web Workers 的概念,它有自己的执行线程。但这与 Promises 无关。

结果存储在回调队列中

它不是存储在回调队列中的异步代码执行的结果。 Promise API 在 Promise 解决时将 Promise Reaction Record 存储在队列中。对于Promise.resolve(),这实际上是同步脚本执行的一部分。

一旦调用堆栈为空,事件循环就会获取这些值。

这里有一个精度要求:有不同的队列。 Promise Job Queue 的优先级高于其他队列,因此只要 Promise Job Queue 上有条目,就不会处理用户 I/O 事件。这些条目的执行被认为是同一个任务的一部分。

【讨论】:

  • 非常好的答案,尤其是“小心你在这里表达事物的方式”。要正确理解 JavaScript 的异步性,区分调用堆栈、作业队列、事件队列非常关键。 (其实我学到了一些东西,不知道还有专门的promise job queue……!)
猜你喜欢
  • 2017-06-28
  • 2020-12-25
  • 1970-01-01
  • 1970-01-01
  • 2015-07-02
  • 2016-11-20
  • 2021-11-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多