问题 “事件循环队列”和“作业队列”之间有什么区别?


我无法理解以下代码是如何运行的。为什么“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

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


5201
2017-11-30 04:38


起源

Promise是异步的 - 即使是像这样的内联同步代码,.then也是异步调用的 - 这就是promises所做的事情。 - Jaromanda X


答案:


为什么“1”在“b”之后?

承诺规范,所有承诺 .then() 在JS的当前线程运行完成之后,异步调用处理程序。因此,两者 a 和 b 作为当前JS的一部分同步执行的将在任何之前执行 .then() 处理程序如此 1 永远都会追随 a 和 b

一些有趣的读物:T问,微任务,队列和时间表 和 在javascript承诺中执行的顺序是什么 和 编写JavaScript框架 - 执行时间,超出setTimeout


这个帖子里有一些很好的建议:承诺在两者之间摇摆不定 nextTick 和 setImmediate

我不建议依赖于确切的执行顺序   非链式事件。如果要控制执行顺序 -   以某种方式重新排列回调,以便成为您想要的回调   稍后执行取决于您要执行的那个   更早,或实现一个队列(在幕后做同样的事情)。

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


5
2017-11-30 04:56



为什么是 h 后 3 :p - Jaromanda X
@JaromandaX,很可能JS实现的作业队列比浏览器实现的事件队列具有更高的优先级。但不是很确定。 - MinusFour
@JaromandaX - 这与OP提出的问题略有不同。我必须做一些研究,以确定这是基于规范还是基于如何实现 .then() 处理程序排队与 setTimeout() 事件。 - jfriend00
@JaromandaX Promise会将作业添加到作业队列,但是当时间到期时,setTimeout会将该函数置于事件循环的末尾。当作业添加到作业队列时,javascript引擎将占用作业队列上的下一个作业,并且仅当作业队列为空时,它才会移动到事件循环上的下一个条目。事件循环上的每个插槽都有自己的作业队列 - balajisoundar
@JaromandaX - 任务与微任务。看起来排序是按照惯例(并没有普遍遵循),而不是规范。 - jfriend00


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

运行计时器回调的任务来自 计时器任务源 并进入同一队列。我们叫这个队列 任务队列“A”

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

从理论上讲,浏览器可以从队列A或B中选择任务来运行,但实际上也是如此 promise任务队列获得更高的优先级,并在计时器回调运行之前清空。

这就是最后记录“h”的原因。诺言 then 调用履行的promises将作业放在promise队列中,该队列的执行优先级高于计时器回调。承诺队列之后才变为空 console.log(3) 已执行,允许定时器回调执行。


高级

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

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

理解承诺不需要微任务排队的细节。

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


5
2017-11-30 07:24





我发现这个对JS新手很容易理解。

这是来自@ getify的书的复制粘贴

使用一个比喻:事件循环队列就像一个游乐园骑行,一旦你完成骑行,你必须再去线路后面再骑。但是工作队列就像完成骑行一样,但随后排成一排并重​​新开始。

事件循环队列 - 对于除promise之外的所有异步回调,h

作业队列 - 用于与promises相关的所有异步回调。 1,2,3

同步 - a,b


1
2018-01-28 17:28