"event loop queue" 和 "job queue" 有什么区别?

What is the difference between "event loop queue" and "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的异步操作之后。为什么?

Why "1" is after "b"?

promise 规范声明所有 promise .then() 处理程序必须在调用堆栈清空后异步调用。因此,在调用堆栈上同步执行的 ab 将在任何 .then() 处理程序之前执行,因此 1 将始终在 ab.

一些有趣的阅读:

  1. Tasks, microtasks, queues and schedules.
  2. Writing a JavaScript framework - Execution timing, beyond setTimeout.

线程“Promises wiggle their way between nextTick and setImmediate”中有一些很好的建议:

I would not recommend relying on the exact execution order of non-chained events. If you want to control the execution order - rearrange the callbacks in a way so that the one that you want to be executed later depends on the one that you want to be executed earlier, or implement a queue (that does the same behind the hood).

换句话说,如果您依赖于异步事件的特定顺序,那么您实际上应该将它们链接起来,而不是在实现中依赖未指定的调度。

在 HTML 术语中,event loop for a page or set of pages from the same domain can have multiple task queues. Tasks from the same task source 总是进入同一个队列,由浏览器选择下一个要使用的任务队列。

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

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

理论上浏览器可以从队列 A 或 B 中选择任务到 运行,但实际上 promise 任务队列获得更高的优先级,并且在计时器回调获得 运行.

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


高级

ECMAScript 守护者选择不在他们的规范中使用 HTML5 术语或任务队列描述,因为 ECMAScript 可以 运行 在更多环境中,而不仅仅是 HTML 浏览器。

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

理解承诺不需要详细了解微任务队列。

用于缺乏对 promises 的原生支持的浏览器(所有版本的 IE 等)的 Promise polyfills 可能会使用计时器,并且在涉及 promise 反应和计时器回调的顺序时,其行为方式与本机实现的方式不完全相同。

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

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

to use a metaphor: the event loop queue is like an amusement park ride, where once you finish the ride, you have to go to the back of the line to ride again. But the Job queue is like finishing the ride, but then cutting in line and getting right back on.

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

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

同步 - a, b

从 Es6 开始,添加了作业队列运行时以适应 promise。使用 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

In Javascript 运行time Job Queue 是在 Java Script 运行time 期间使用 Task Queue 创建的,它与 task queue 非常相似但具有优先级在任务队列上,这意味着 javascript 事件循环首先查看作业队列,如果作业队列中有任何任务,事件循环将检查堆栈,如果堆栈为空,它将从作业中将任务推入堆栈事件循环之后的队列如果作业队列为空,则再次检查作业队列现在事件循环检查任务队列,如果有任何任务,任务将被推送到堆栈以执行。作业队列添加在 Java Script 运行 时间在 es6 中用于执行 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 和 promise 都是异步执行的,并且都需要相同的时间,但是 promises 先执行然后回调,这是因为 promise 被移动到作业队列,而 setTimeout 被移动到来自 Web 的回调队列 Api 基本上是在 javascript 运行时创建的,以异步执行任务,因此作业队列优先于任务队列

ES6 有 2 个队列

  1. 回调队列
  2. Job queue(微任务队列)

setTimeout 和 promise 都是异步代码。

在 setTimeout 中,当后台网络浏览器 api 工作完成时,我们明确指定函数为自动 运行(在 setTimeout 的情况下,它是网络浏览器的定时器功能 api ), 一旦计时器完成它的工作,它将函数推送到 回调队列 并且必须等到 js 的所有同步代码完成,这就是为什么

console.log("a")
console.log("b")

先完成

现在来到 promise 部分,JS 中的任何 promise 都会做两件事

  1. 设置背景api功能
  2. returns 承诺对象

.then() 指定哪个函数 运行 一旦 promise 已经解决,但不是立即

.then() 中指定的函数在任务完成时被推送到 作业队列

一旦 JS 中的所有同步代码完成,事件循环首先检查作业队列,然后检查回调队列 这就是为什么 'h' 被记录在最后