"事件循环队列"和"作业队列"之间有什么区别?

Sam*_*ang 21 javascript event-loop job-queue es6-promise

我无法理解以下代码是如何运行的.为什么"1"在"b"之后但"h"在"3"之后?订单应该是:a,b,1,2,h,3?有些文章说"事件循环队列"和"作业队列"之间的区别导致了以下输出.但是怎么样?我已经阅读了ECMAScript 2015 - 8.4工作和工作队列的规范,想知道Promise'job是如何工作的,但这让我更加困惑.有人能帮我吗?谢谢!

var promise = new Promise(function(resolve, reject) {resolve(1)});
promise.then(function(resolve) {console.log(1)});
console.log('a');
promise.then(function(resolve) {console.log(2);});
setTimeout(function() {console.log('h')}, 0);
promise.then(function(resolve) {console.log(3)});
console.log('b');

// a
// b
// 1
// 2
// 3
// h
Run Code Online (Sandbox Code Playgroud)

我知道Promise是异步的,但setTimeout(..)异步操作的回调总是在Promise的异步操作之后.为什么?

jfr*_*d00 15

为什么"1"在"b"之后?

通过promise规范,所有promise .then()处理程序在JS的当前线程运行完成后异步调用.因此,作为当前JS的一部分同步执行的ab将在任何.then()处理程序之前执行,因此1将始终在a和之后执行b.

一些有趣的阅读:T 问,微任务,队列和日程安排以及javascript承诺编写JavaScript框架的执行顺序是什么- 执行时间,超出setTimeout.


这个帖子里有一些很好的建议:Promises在nextTick和之间摆动setImmediate:

我不建议依赖非链式事件的确切执行顺序.如果要控制执行顺序 - 以某种方式重新排列回调,以便稍后要执行的回调取决于您之前要执行的那个,或者实现一个队列(在引擎盖后面做同样的事情) ).

换句话说,如果您依赖于异步事件的特定时间,那么您实际上应该在它们的代码中将它们链接起来,因此必须通过代码在另一个之后发生,而不是依赖于实现中的未指定的调度.

  • @JaromandaX Promise会将作业添加到作业队列,但是setTimeout将在时间到期时将函数置于事件循环的末尾。当将作业添加到作业队列时,javascript引擎将在作业队列中接下一个作业,并且只有当作业队列为空时,它才会移动到事件循环中的下一个条目。事件循环上的每个插槽都有自己的作业队列 (5认同)
  • @JaromandaX,很可能JS实现的作业队列比浏览器实现的事件队列具有更高的优先级。但不太确定。 (2认同)
  • @JaromandaX-[任务与微任务](https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/)。看起来顺序是按照惯例(不是普遍遵循的)而不是规范。 (2认同)

tra*_*r53 11

在HTML术语中,来自同一域的页面或一组页面的事件循环可以具有多个任务队列.来自同一任务源任务始终进入同一队列,浏览器选择下一个要使用的任务队列.

运行计时器回调的任务来自计时器任务源并进入同一队列.我们将此队列任务队列称为"A".

ECMAscript 2015(ES6)规范要求任务运行Promise反应回调以形成称为"PromiseJobs"的自己的作业队列.ECMAscript和HTML规范不使用相同的语言,因此我们在概念上将ECMA的"Promise Job queue"与浏览器中的HTML 任务队列"B"等同起来- 至少与计时器使用的队列不同.

从理论上讲,浏览器可以从队列A或B中选择要运行的任务,但实际上,承诺任务队列获得更高的优先级,并且在计时器回调运行之前将被清空.

这就是最后记录"h"的原因.then对履行承诺的Promise 调用将作业放在promise队列中,该队列以比计时器回调更高的优先级执行.承诺队列console.log(3)在执行后才变为空,这允许定时器回调执行.


高级

ECMAScript监护人选择不在其规范中使用HTML5术语或任务队列描述,因为ECMAScript可以在比HTML浏览器更多的环境中运行.

承诺队列的本机实现可以使用"微任务"队列而不是单独的专用承诺任务队列.在当前脚本线程和先前添加到微队列的任何任务完成之后,只需运行微排队作业.

了解承诺不需要微任务排队的细节.

对于缺乏对promises的本机支持(所有IE版本等)的浏览器,承诺polyfill可能会使用计时器,并且当涉及到promise响应和计时器回调的顺序时,其行为与本机实现完全不同.


Yil*_*maz 10

从ES6开始,添加了作业队列运行时来适应承诺。我们可以new Promise()在本地处理异步代码。setTimeout不是 JavaScript 的一部分;它是浏览器提供的 Web API 的一部分。

现在我们有两个队列。回调队列作业队列。作业队列也称为微任务队列。

关键是,作业队列比回调队列具有更高的优先级。因此,在您的示例中,将执行第一个同步代码。

 console.log('a');  // a
 console.log('b');  // b
Run Code Online (Sandbox Code Playgroud)

然后 Promise 被发送到作业队列,setTimeout() 被发送到回调队列。现在,无论 setTimeout() 设置了多长时间,事件循环都会首先检查作业队列。由于队列实现“先进先出”,因此它们会按顺序执行,因为它们只是登录到控制台。

promise.then(function(resolve) {console.log(1)}); // 1
promise.then(function(resolve) {console.log(2)}); // 2
promise.then(function(resolve) {console.log(3)}); // 3
Run Code Online (Sandbox Code Playgroud)

清除作业队列后,事件循环检查回调队列:

setTimeout(function() {console.log('h')}, 0); // h
Run Code Online (Sandbox Code Playgroud)