这些代码语句以什么顺序移动到调用堆栈?

In which order these code statements are moved to call stack?

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

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

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

console.log("Hello");

整个 promise 链是移动到调用堆栈还是单个 thens?

Does the whole promise chain move to the call stack or individual thens ?

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

链式 then 方法在不同的 promise 对象上执行,每个对象都会在 Promise Job Queue 中排队一个 reaction 处理程序, 但是 只有在 相应的 promise 解决。

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

承诺 A 得到解决后,将在承诺作业队列中放入一个条目。我们称其为 H(A) ("Handler for reacting to resolved promise A")

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

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

调用堆栈再次为空。

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

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

更现实的情况是,像 function2 return 这样的函数是调用异步 API 的结果,例如 fetch.

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

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

然后事件循环将检测作业队列中何时有内容(如 H(E) )。

一些其他备注

executed in a separate Web API thread

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

也就是说,可能有 API 涉及非 JS 部分,例如访问 OS 函数,这些函数可能通过其他(非 JS)线程执行。还有
的概念 Web Workers里面得到自己的执行线程。但这与 Promises 无关。

the result is stored in a callback queue

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

those values are picked up by the event loop once the call stack is empty.

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