javascript 的 Promise、事件循环和作业队列

2023-11-22

考虑以下代码:

function foo() {
    console.log('foo');

    new Promise(
        function(resolve, reject) {
            setTimeout(function() {
                resolve('RESOLVING');
            }, 5000);
        }
    )
    .then(
        function(value) {
            console.log(value);
        }
    );
}
foo();

我试图正确理解这里发生的事情:

  1. 在执行时new Promise“执行器函数”直接运行,当setTimeout被调用时,将安排一个向“事件队列”添加新条目的操作(5 秒后)
  2. 因为打电话给then将对传递的函数(记录到控制台)的调用添加到“作业队列”的操作被组织为在Promise已解决
  3. 当。。。的时候setTimeout执行回调(在事件循环的某个时间点上),Promise已解决并基于第 2 点,即函数参数then调用被添加到“作业队列”并随后执行。

请注意,我说“作业队列”是因为有些事情我不确定;哪个“作业队列是?”。据我理解,“作业队列”链接到“事件队列”上的条目。那么这会是setTimeout上面例子中的条目? 假设之前(和之后)没有其他事件添加到“事件队列”中setTimeout添加了 的回调,到那时主代码的条目(对 foo 的调用)是否已经(通常)消失(运行至完成),因此除了setTimeout是为了then要链接到的“作业队列”条目?


  1. 在执行时new Promise“执行器函数”直接运行,当setTimeout被调用时,将安排一个向“事件队列”添加新条目的操作(5 秒后)

是的。更具体地说,调用setTimeout使用浏览器的定时器机制安排定时器;大约五秒钟后,计时器机制将一个作业添加到主作业队列中,该队列将调用您的回调。

  1. 因为打电话给then将对传递的函数(记录到控制台)的调用添加到“作业队列”的操作被组织为在Promise已解决

Right. then(使用单个参数)向 Promise 添加一个履行处理程序(并创建它返回的另一个 Promise)。当承诺解决时,调用处理程序的作业将被添加到作业队列中(但它是一个不同的作业队列)。

  1. 当。。。的时候setTimeout执行回调(在事件循环的某个时间点上),Promise已解决并基于第 2 点,即函数参数then调用被添加到“作业队列”并随后执行。

是的,但不是同一个作业队列。 :-)

主作业队列是事件处理程序和计时器回调等的所在。主事件循环从队列中选取一个作业,运行它直至完成,然后选取下一个作业,依此类推,如果没有要运行的作业,则空闲。

一旦作业运行完成,another循环运行,它负责运行在该主作业期间安排的任何待处理的承诺作业。

在 JavaScript 规范中,主作业队列称为 ScriptJobs,承诺回调作业队列称为 PromiseJobs。在 ScriptJob 结束时,所有已排队的 PromiseJobs 将在下一个 ScriptJob 之前执行。 (在 HTML 规范中,它们的名称是“任务”[或“宏任务”] 和“微任务”。)

是的,这确实意味着如果作业 A 和作业 B 都已排队,然后作业 A 被选取并安排承诺回调,则运行该承诺回调before即使作业 B 首先排队(在主队列中),作业 B 仍会运行。

请注意,我说“作业队列”是因为有些事情我不确定;这是哪个“作业队列?”

希望我上面已经介绍过了。基本上:

  • 初始脚本执行、事件处理程序、计时器和requestAnimationFrame回调会排队到 ScriptJobs 队列(主要队列);它们是“宏任务”(或简称“任务”)。
  • Promise 回调会排队到 PromiseJobs 队列中,该队列会一直被处理,直到任务结束时为空。也就是说,承诺回调是“微任务”。

据我理解,“作业队列”链接到“事件队列”上的条目。

这些只是同一事物的不同名称。 JavaScript 规范使用术语“作业”和“作业队列”。 HTML 规范使用“任务”、“任务队列”和“事件循环”。事件循环负责从 ScriptJobs 队列中获取作业。

那么这会是setTimeout上面例子中的条目?

当计时器触发时,作业将被安排在 ScriptJobs 队列中。

假设之前(和之后)没有其他事件添加到“事件队列”中setTimeout添加了 的回调,到那时主代码的条目(对 foo 的调用)是否已经(通常)消失(运行至完成),因此除了setTimeout是为了then要链接到的“作业队列”条目?

基本上是的。让我们来看看:

  • 浏览器加载脚本并向 ScriptJobs 添加作业以运行脚本的顶级代码。
  • The event loop picks up that job and runs it:
    • 该代码定义了foo并调用它。
    • Within foo,你做console.log然后创建一个承诺。
    • Promise 执行器安排一个计时器回调:将计时器添加到浏览器的计时器列表中。它尚未对作业进行排队。
    • then向承诺添加履行处理程序。
    • 那个工作就结束了。
  • 大约五秒钟后,浏览器会向 ScriptJobs 添加一个作业来调用计时器回调。
  • The event loop picks up that job and runs it:
    • 回调解析 Promise,这会将 Promise 履行处理程序调用添加到 PromiseJobs 队列。
    • That job ends, but with entries in PromiseJobs, so the JavaScript engine loops through those in order:
      • 它获取履行处理程序回调作业并运行它;履行处理程序所做的console.log
    • 这项工作现在已经完全完成了。

更多探索:

  • JavaScript 规范中的作业和作业队列
  • HTML5 规范中的事件循环
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

javascript 的 Promise、事件循环和作业队列 的相关文章

随机推荐