簡體   English   中英

“事件循環隊列”和“作業隊列”有什么區別?

[英]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 的異步操作之后。 為什么?

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

根據promise 規范,所有promise .then()處理程序在JS 的當前線程運行完成.then()異步調用。 因此,作為當前 JS 的一部分同步執行的ab將在任何.then()處理程序之前執行.then()因此1將始終在ab

一些有趣的閱讀:T asks, microtasks, queues and schedulesWhat is the order of execution in javascript promisesWriting a JavaScript framework - Execution Timing, Beyond setTimeout


在這個線程中有一些很好的建議:承諾在nextTicksetImmediate之間擺動

我不建議依賴非鏈式事件的確切執行順序。 如果您想控制執行順序 — 以某種方式重新排列回調,以便您希望稍后執行的回調取決於您希望較早執行的回調,或者實現一個隊列(在幕后執行相同的操作) )。

換句話說,如果您依賴於異步事件的特定時間,那么您實際上應該將它們鏈接到您的代碼中,這樣一個事件必須通過您的代碼一個接一個發生,而不是依賴於實現中未指定的調度。

在 HTML 術語中,來自同一域的一個頁面或一組頁面的事件循環可以有多個任務隊列 來自同一個任務源任務總是進入同一個隊列,瀏覽器選擇下一個要使用的任務隊列。

運行計時器回調的任務來自計時器任務源並進入同一隊列。 我們將此隊列任務隊列稱為“A”

ECMAscript 2015 (ES6) 規范要求任務運行 Promise 反應回調以形成它們自己的名為“PromiseJobs”的作業隊列。 ECMAscript 和 HTML 規范不使用相同的語言,所以讓我們在概念上將 ECMA 的“Promise Job 隊列”等同於瀏覽器中的 HTML任務隊列“B” ——至少與計時器使用的隊列不同。

理論上,瀏覽器可以從隊列 A 或 B 中選擇任務來運行,但實際上承諾任務隊列獲得更高的優先級,並且會在計時器回調運行之前清空。

這就是為什么最后記錄“h”的原因。 Promise then調用已完成的 Promise 將作業放在 Promise 隊列中,這些任務以比計時器回調更高的優先級執行。 只有在console.log(3)執行后,promise 隊列才會變空,這允許定時器回調執行。


先進的

ECMAScript 的守護者選擇不在他們的規范中使用 HTML5 術語或任務隊列描述,因為 ECMAScript 可以在更多環境中運行,而不僅僅是 HTML 瀏覽器。

承諾隊列的本機實現可以使用“微任務”隊列而不是單獨的專用承諾任務隊列。 微隊列作業只是在當前腳本線程和之前添加到微隊列的任何任務完成后運行。

理解promise不需要微任務隊列的細節。

對於缺乏原生支持承諾的瀏覽器(所有版本的 IE 等)的 Promise polyfills 可能會使用計時器,並且在涉及 Promise 反應和計時器回調的順序時,其行為方式與本機實現的方式不同。

我發現這個對於 JS 新手來說很容易理解。

這是@getify 書中的復制粘貼

用一個比喻:事件循環隊列就像一個游樂園的游樂設施,一旦你完成了游樂設施,你必須到隊伍的后面再騎一次。 但是工作隊列就像完成騎行,然后插隊並重新開始。

事件循環隊列 - 對於除 promises 之外的所有異步回調,h

作業隊列 - 所有與承諾相關的異步回調。 1、2、3

同步 - a, b

ES6 有 2 個隊列

  1. 回調隊列
  2. 作業隊列(微任務隊列)

setTimeout 和 promise 都是異步代碼。

在 setTimeout 中,我們明確指定當后台 Web 瀏覽器 api 工作完成時要自動運行的函數(在 setTimeout 的情況下,它是 Web 瀏覽器 api 的 Timer 功能),一旦定時器完成它的工作,它會將函數推送到回調隊列並且必須等到js的所有同步代碼完成,這就是為什么

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

先完成

現在來到承諾部分,JS 中的任何承諾都會做兩件事

  1. 設置后台api功能
  2. 返回一個承諾對象

.then()指定一旦 promise 被解決后要運行的函數,但不是立即運行

.then()中指定的函數在任務完成時被推送到作業隊列

一旦 JS 中的所有同步代碼完成,事件循環首先檢查作業隊列,然后檢查回調隊列所以這就是為什么最后會記錄 'h'

從 Es6 開始,添加了作業隊列運行時以適應承諾。 使用new Promise()我們本地處理異步代碼。 setTimeout不是 javascript 的一部分,它是瀏覽器提供的 web 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

在 Javascript 運行時作業隊列是在 Java 腳本運行時使用任務隊列創建的,它與任務隊列非常相似,但優先於任務隊列,這意味着如果作業中有任何任務,javascript 事件循環首先查看作業隊列排隊事件循環將檢查堆棧如果堆棧為空它將在該事件循環之后將任務從作業隊列推送到堆棧中如果作業隊列為空則再次檢查作業隊列現在事件循環檢查任務隊列是否有任何任務將執行被壓入堆棧執行。 在 es6 的 Java Script Run time 中添加了作業隊列,用於 promise 的執行

例如 :

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 都是異步執行的,並且都花費相同的時間,但是 promise 先執行然后回調,這是因為 promise 被移動到作業隊列,而 setTimeout 被移動到 Web Api 的回調隊列,這基本上是在 javascript 運行時創建的異步執行任務,因此作業隊列優先於任務隊列

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM