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

时间:2016-11-30 04:38:10

标签: javascript es6-promise event-loop job-queue

我无法理解以下代码是如何运行的。为什么“1”在“b”之后但“h”在“3”之后?订单应该是:a,b,1,2,h,3?有些文章说“事件循环队列”和“作业队列”之间的区别导致了以下输出。但是怎么样?我已经阅读了ECMAScript 2015 - 8.4 Jobs and Job Queues的规范,想知道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的异步操作之后。为什么呢?

5 个答案:

答案 0 :(得分:11)

  

为什么" 1"是在" b"?

之后

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

一些有趣的读物:T asks, microtasks, queues and schedulesWhat is the order of execution in javascript promises以及Writing a JavaScript framework - Execution timing, beyond setTimeout

在这个帖子中有一些很好的建议:Promises wiggle their way between nextTick and setImmediate

  

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

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

答案 1 :(得分:9)

在HTML术语中,来自同一域的一个页面或一组页面的event loop可以有多个task queues。来自同一task source的任务始终进入同一队列,浏览器选择下一个要使用的任务队列。

运行计时器回调的任务来自timer task source并进入同一队列。让我们将此队列称为任务队列" A"

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

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

这就是为什么" h"最后记录。 Promise then调用已完成的promise将作业放在promise队列中,该队列的执行优先级高于计时器回调。承诺队列仅在执行console.log(3)后变为空,这允许定时器回调执行。

<小时/> 高级

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

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

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

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

答案 2 :(得分:1)

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

这是@ getify的书中的复制粘贴

  

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

事件循环队列 - 用于承诺以外的所有异步回调,h

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

同步 - a,b

答案 3 :(得分:0)

从Es6开始,添加了作业队列运行时以适应承诺。使用new Promise(),我们可以本地处理异步代码。 setTimeout不是javascript的一部分,它是浏览器提供的网络api的一部分。

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

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

 console.log('a');  // a
 console.log('b');  // b

然后将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 

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

setTimeout(function() {console.log('h')}, 0); // h

答案 4 :(得分:0)

在 Javascript 运行时作业队列是在 Java Script 运行时使用任务队列创建的,它与任务队列非常相似,但优先于任务队列,这意味着如果有任何任务,javascript 事件循环首先查看作业队列在作业队列中,事件循环将检查堆栈,如果堆栈为空,它将在事件循环之后将任务从作业队列推送到堆栈中,如果作业队列为空,则再次检查作业队列现在事件循环检查任务队列是否有任何任务任务将被推送到堆栈以执行。在 es6 的 Java Script Run time 中添加作业队列,用于 promises 的执行

例如:

var promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('promise win')
    }, 4000)
})
promise.then((result) => {
    console.log(result)
})
setTimeout(() => {
    console.log('setTimeout win')
}, 4000)

输出:

承诺胜利

setTimeout 获胜

说明:

setTimeout 和 promise 都是异步执行的,并且都需要相同的时间,但是 promise 先执行然后回调,这是因为 promise 被移动到作业队列,而 setTimeout 被移动到 Web Api 的回调队列,这基本上是创建在javascript运行时异步执行任务,因此作业队列优先于任务队列