繁体   English   中英

javascript promises,事件循环和作业队列

[英]javascript promises, the event loop, and the job queue

请考虑以下代码:

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的调用)是否(通常)已经消失(运行完成)那个时候因为then的“作业队列”条目没有其他条目而不是setTimeout的链接到?

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

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

  1. 因为调用then添加到“作业队列”的操作,在Promise解决后组织调用传递的函数(记录到控制台)

对。 then (使用单个参数)将一个履行处理程序添加到promise(并创建它返回的另一个promise)。 当promise解析时,调用处理程序的作业将添加到作业队列中(但它是一个不同的作业队列)。

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

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

主要的作业队列就是事件处理程序和计时器回调等等。 主事件循环从队列中获取作业,将其运行至完成,然后选择下一个作业等,如果没有要运行的作业则空闲。

作业运行完成后,将运行另一个循环,该循环负责运行在该主作业期间计划的任何待处理的承诺作业。

在JavaScript规范中,主作业队列称为ScriptJobs,promise回调作业队列为PromiseJobs。 在ScriptJob结束时,所有已排队的PromiseJobs都会在下一个ScriptJob之前执行。 (在HTML规范中,它们的名称是“task”[或“macrotask”]和“microtask”。)

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

注意我说[一个“工作队列”],因为有一些我不确定的事情; 哪个“工作队列呢?”

希望我已经涵盖了上述内容。 基本上:

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

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

对于同样的事情,这些只是不同的名称。 JavaScript规范使用术语“作业”和“作业队列”。 HTML规范使用“任务”和“任务队列”和“事件循环”。 事件循环是从ScriptJobs队列中获取作业的。

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

当计时器触发时,作业将在ScriptJobs队列中进行调度。

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

基本上是的。 让我们把它搞定:

  • 浏览器加载脚本并向ScriptJobs添加作业以运行脚本的顶级代码。
  • 事件循环选择该作业并运行它:
    • 该代码定义了foo并调用它。
    • foo ,您执行console.log ,然后创建一个promise。
    • promise执行程序调度计时器回调:它将一个计时器添加到浏览器的计时器列表中。 它还没有排队工作。
    • then在promise中添加一个履行处理程序。
    • 那份工作结束了。
  • 大约五秒钟后,浏览器向ScriptJobs添加一个作业来调用您的计时器回调。
  • 事件循环选择该作业并运行它:
    • 回调解析了promise,它向PromiseJobs队列添加了promise履行处理程序调用。
    • 该作业结束,但PromiseJobs中的条目,所以JavaScript引擎按顺序循环遍历:
      • 它获取履行处理程序回调作业并运行它; 履行处理程序执行console.log
    • 那项工作现在完全完成了。

更多探索:

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM