整个承诺链是移动到调用堆栈还是单独的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),...,所以它继续。
如果function2、function3 和function4 返回 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 事件。这些条目的执行被认为是同一个任务的一部分。