這篇文章給大家分享的是有關Node.js中回調隊列的案例分析的內容。小編覺得挺實用的,因此分享給大家做個參考。一起跟隨小編過來看看吧。
為白云等地區用戶提供了全套網頁設計制作服務,及白云網站建設行業解決方案。主營業務為網站建設、做網站、白云網站設計,以傳統方式定制建設網站,并提供域名空間備案等一條龍服務,秉承以專業、用心的態度為用戶提供真誠的服務。我們深信只要達到每一位用戶的要求,就會得到認可,從而選擇與我們長期合作。這樣,我們也可以走得更遠!隊列是 Node.js 中用于有效處理異步操作的一項重要技術。
在本文中,我們將深入研究 Node.js 中的隊列:它們是什么,它們如何工作(通過事件循環)以及它們的類型。
隊列是 Node.js 中用于組織異步操作的數據結構。這些操作以不同的形式存在,包括HTTP請求、讀取或寫入文件操作、流等。
在 Node.js 中處理異步操作非常具有挑戰性。
HTTP 請求期間可能會出現不可預測的延遲(或者更糟糕的可能性是沒有結果),具體取決于網絡質量。嘗試用 Node.js 讀寫文件時也有可能會產生延遲,具體取決于文件的大小。
類似于計時器和其他的許多操作,異步操作完成的時間也有可能是不確定的。
在這些不同的延遲情況之下,Node.js 需要能夠有效地處理所有這些操作。
Node.js 無法處理基于 first-start-first-handle (先開始先處理)或 first-finish-first-handle (先結束先處理)的操作。
之所以不能這樣做的一個原因是,在一個異步操作中可能還會包含另一個異步操作。
為第一個異步過程留出空間意味著必須先要完成內部異步過程,然后才能考慮隊列中的其他異步操作。
有許多情況需要考慮,因此最好的選擇是制定規則。這個規則影響了事件循環和隊列在 Node.js 中的工作方式。
讓我們簡要地看一下 Node.js 是怎樣處理異步操作的。
調用棧被用于跟蹤當前正在執行的函數以及從何處開始運行。當一個函數將要執行時,它會被添加到調用堆棧中。這有助于 JavaScript 在執行函數后重新跟蹤其處理步驟。
回調隊列是在后臺操作完成時把回調函數保存為異步操作的隊列。它們以先進先出(FIFO)的方式工作。我們將會在本文后面介紹不同類型的回調隊列。
請注意,Node.js 負責所有異步活動,因為 JavaScript 可以利用其單線程性質來阻止產生新的線程。
在完成后臺操作后,它還負責向回調隊列添加函數。 JavaScript 本身與回調隊列無關。同時事件循環會連續檢查調用棧是否為空,以便可以從回調隊列中提取一個函數并添加到調用棧中。事件循環僅在執行所有同步操作之后才檢查隊列。
那么,事件循環是按照什么樣的順序從隊列中選擇回調函數的呢?
首先,讓我們看一下回調隊列的五種主要類型。
IO操作是指涉及外部設備(如計算機的硬盤、網卡等)的操作。常見的操作包括讀寫文件操作、網絡操作等。這些操作應該是異步的,因為它們留給 Node.js 處理。
JavaScript 無法訪問計算機的內部設備。當執行此類操作時,JavaScript 會將其傳輸到 Node.js 以在后臺處理。
完成后,它們將會被轉移到 IO 回調隊列中,來進行事件循環,以轉移到調用棧中執行。
每個涉及 Node.js 計時器功能的操作(如 setTimeout()
和 setInterval()
)都是要被添加到計時器隊列的。
請注意,JavaScript 語言本身沒有計時器功能。它使用 Node.js 提供的計時器 API(包括 setTimeout
)執行與時間相關的操作。所以計時器操作是異步的。無論是 2 秒還是 0 秒,JavaScript 都會把與時間相關的操作移交給 Node.js,然后將其完成并添加到計時器隊列中。
例如:
setTimeout(function() { console.log('setTimeout'); }, 0) console.log('yeah') # 返回 yeah setTimeout
在處理異步操作時,JavaScript 會繼續執行其他操作。只有在所有同步操作都已被處理完畢后,事件循環才會進入回調隊列。
該隊列分為兩個隊列:
process.nextTick
函數而延遲的函數。事件循環執行的每個迭代稱為一個 tick(時間刻度)。
process.nextTick
是一個函數,它在下一個 tick (即事件循環的下一個迭代)執行一個函數。微任務隊列需要存儲此類函數,以便可以在下一個 tick 執行它們。
這意味著事件循環必須繼續檢查微任務隊列中的此類函數,然后再進入其他隊列。
promises
而延遲的函數。如你所見,在 IO 和計時器隊列中,所有與異步操作有關的內容都被移交給了異步函數。
但是 promise 不同。在 promise 中,初始變量存儲在 JavaScript 內存中(你可能已經注意到了<Pending>
)。
異步操作完成后,Node.js 會將函數(附加到 Promise)放在微任務隊列中。同時它用得到的結果來更新 JavaScript 內存中的變量,以使該函數不與 <Pending>
一起運行。
以下代碼說明了 promise 是如何工作的:
let prom = new Promise(function (resolve, reject) { // 延遲執行 setTimeout(function () { return resolve("hello"); }, 2000); }); console.log(prom); // Promise { <pending> } prom.then(function (response) { console.log(response); }); // 在 2000ms 之后,輸出 // hello
關于微任務隊列,需要注意一個重要功能,事件循環在進入其他隊列之前要反復檢查并執行微任務隊列中的函數。例如,當微任務隊列完成時,或者說計時器操作執行了 Promise 操作,事件循環將會在繼續進入計時器隊列中的其他函數之前參與該 Promise 操作。
因此,微任務隊列比其他隊列具有最高的優先級。
檢查隊列也稱為即時隊列(immediate queue)。IO 隊列中的所有回調函數均已執行完畢后,立即執行此隊列中的回調函數。setImmediate
用于向該隊列添加函數。
例如:
const fs = require('fs'); setImmediate(function() { console.log('setImmediate'); }) // 假設此操作需要 1ms fs.readFile('path-to-file', function() { console.log('readFile') }) // 假設此操作需要 3ms do...while...
執行該程序時,Node.js 把 setImmediate
回調函數添加到檢查隊列。由于整個程序尚未準備完畢,因此事件循環不會檢查任何隊列。
因為 readFile
操作是異步的,所以會移交給 Node.js,之后程序將會繼續執行。
do while
操作持續 3ms。在這段時間內,readFile
操作完成并被推送到 IO 隊列。完成此操作后,事件循環將會開始檢查隊列。
盡管首先填充了檢查隊列,但只有在 IO 隊列為空之后才考慮使用它。所以在 setImmediate
之前,將 readFile
輸出到控制臺。
此隊列存儲與關閉事件操作關聯的函數。
包括以下內容:
這些隊列被認為是優先級最低的,因為此處的操作會在以后發生。
你肯sing不希望在處理 promise 函數之前在 close 事件中執行回調函數。當服務器已經關閉時,promise 函數會做些什么呢?
微任務隊列具有最高優先級,其次是計時器隊列,I/O隊列,檢查隊列,最后是關閉隊列。
讓我們通過一個更復雜的例子來說明隊列的類型和順序:
const fs = require("fs"); // 假設此操作需要 2ms fs.writeFile('./new-file.json', '...', function() { console.log('writeFile') }) // 假設這需要 10ms 才能完成 fs.readFile("./file.json", function(err, data) { console.log("readFile"); }); // 不需要假設,這實際上需要 1ms setTimeout(function() { console.log("setTimeout"); }, 1000); // 假設此操作需要 3ms while(...) { ... } setImmediate(function() { console.log("setImmediate"); }); // 解決 promise 需要 4 ms let promise = new Promise(function (resolve, reject) { setTimeout(function () { return resolve("promise"); }, 4000); }); promise.then(function(response) { console.log(response) }) console.log("last line");
程序流程如下:
fs.writeFile
在后臺花費 2 毫秒。fs.readFile
takes 10ms at the background before Node.js adds the callback function to the IO queue.
fs.readFile
在后臺花費 10 毫秒。setTimeout
在后臺花費 1ms。setTimeout
和 fs.writeFile
操作完成,并將它們的回調函數分別添加到計時器和 IO 隊列中。現在的隊列是:
// queues Timer = [ function () { console.log("setTimeout"); }, ]; IO = [ function () { console.log("writeFile"); }, ];
setImmediate
將回調函數添加到 Check 隊列中:
js // 隊列 Timer... IO... Check = [ function() {console.log("setImmediate")} ]
在將 promise 操作添加到微任務隊列之前,需要花費 4ms 的時間在后臺進行解析。
最后一行是同步的,因此將會立即執行:
# 返回 "last line"
因為所有同步活動都已完成,所以事件循環開始檢查隊列。由于微任務隊列為空,因此它從計時器隊列開始:
// 隊列 Timer = [] // 現在是空的 IO... Check... # 返回 "last line" "setTimeout"
當事件循環繼續執行隊列中的回調函數時,promise
操作完成并被添加到微任務隊列中:
// 隊列 Timer = []; Microtask = [ function (response) { console.log(response); }, ]; IO = []; // 當前是空的 Check = []; // 當前是在 IO 的后面,為空 # results "last line" "setTimeout" "writeFile" "setImmediate"
幾秒鐘后,readFile
操作完成,并添加到 IO 隊列中:
// 隊列 Timer = []; Microtask = []; // 當前是空的 IO = [ function () { console.log("readFile"); }, ]; Check = []; # results "last line" "setTimeout" "writeFile" "setImmediate" "promise"
最后,執行所有回調函數:
// 隊列 Timer = [] Microtask = [] IO = [] // 現在又是空的 Check = []; # results "last line" "setTimeout" "writeFile" "setImmediate" "promise" "readFile"
這里要注意的三點:
readFile
),事件循環也會執行檢查隊列中的函數。這樣做的原因是此時 IO 隊列為空。請記住,在執行 IO 隊列中的所有的函數之后,將會立即運行檢查隊列回調。JavaScript 是單線程的。每個異步函數都由依賴操作系統內部函數工作的 Node.js 去處理。
Node.js 負責將回調函數(通過 JavaScript 附加到異步操作)添加到回調隊列中。事件循環會確定將要在每次迭代中接下來要執行的回調函數。
了解隊列如何在 Node.js 中工作,使你對其有了更好的了解,因為隊列是環境的核心功能之一。 Node.js 最受歡迎的定義是 non-blocking
(非阻塞),這意味著異步操作可以被正確的處理。都是因為有了事件循環和回調隊列才能使此功能生效。
感謝各位的閱讀!關于Node.js中回調隊列的案例分析就分享到這里了,希望以上內容可以對大家有一定的幫助,讓大家可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!
當前標題:Node.js中回調隊列的案例分析-創新互聯
網站地址:http://m.newbst.com/article48/cogshp.html
成都網站建設公司_創新互聯,為您提供自適應網站、品牌網站設計、全網營銷推廣、Google、品牌網站建設、網站導航
聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯