本文涵蓋
面試題的引入
對事件循環面試題執行順序的一些疑問
通過面試題對微任務、事件循環、定時器等對深入理解
結論總結
面試題面試題如下,大家可以先試著寫一下輸出結果,然后再看我下面的詳細講解,看看會不會有什么出入,如果把整個順序弄清楚 Node.js 的執行順序應該就沒問題了。
async function async1(){ console.log("async1 start") await async2() console.log("async1 end") } async function async2(){ console.log("async2") } console.log("script start") setTimeout(function(){ console.log("setTimeout0") },0) setTimeout(function(){ console.log("setTimeout3") },3) setImmediate(() => console.log("setImmediate")); process.nextTick(() => console.log("nextTick")); async1(); new Promise(function(resolve){ console.log("promise1") resolve(); console.log("promise2") }).then(function(){ console.log("promise3") }) console.log("script end")
面試題正確的輸出結果
script start async1 start async2 promise1 promise2 script end nextTick async1 end promise3 setTimeout0 setImmediate setTimeout3提出問題
在理解node.js的異步的時候有一些不懂的地方,使用node.js的開發者一定都知道它是單線程的,異步不阻塞且高并發的一門語言,但是node.js在實現異步的時候,兩個異步任務開啟了,是就是誰快就誰先完成這么簡單,還是說異步任務最后也會有一個先后執行順序?對于一個單線程的的異步語言它是怎么實現高并發的呢?
好接下來我們就帶著這兩個問題來真正的理解node.js中的異步(微任務與事件循環)。
Node 的異步語法比瀏覽器更復雜,因為它可以跟內核對話,不得不搞了一個專門的庫 libuv 做這件事。這個庫負責各種回調函數的執行時間,異步任務最后基于事件循環機制還是要回到主線程,一個個排隊執行。
詳細講解 1.本輪循環與次輪循環異步任務可以分成兩種。
追加在本輪循環的異步任務
追加在次輪循環的異步任務
所謂”循環”,指的是事件循環(event loop)。這是 JavaScript 引擎處理異步任務的方式,后文會詳細解釋。這里只要理解,本輪循環一定早于次輪循環執行即可。
Node 規定,process.nextTick和Promise的回調函數,追加在本輪循環,即同步任務一旦執行完成,就開始執行它們。而setTimeout、setInterval、setImmediate的回調函數,追加在次輪循環。
2.process.nextTick()1)process.nextTick不要因為有next就被好多小伙伴當作次輪循環。
2)Node 執行完所有同步任務,接下來就會執行process.nextTick的任務隊列。
3)開發過程中如果想讓異步任務盡可能快地執行,可以使用process.nextTick來完成。
3.微任務(microtack)根據語言規格,Promise對象的回調函數,會進入異步任務里面的”微任務”(microtask)隊列。
微任務隊列追加在process.nextTick隊列的后面,也屬于本輪循環。
根據語言規格,Promise對象的回調函數,會進入異步任務里面的”微任務”(microtask)隊列。
微任務隊列追加在process.nextTick隊列的后面,也屬于本輪循環。所以,下面的代碼總是先輸出3,再輸出4。
process.nextTick(() => console.log(3)); Promise.resolve().then(() => console.log(4));
// 輸出結果3,4
process.nextTick(() => console.log(1)); Promise.resolve().then(() => console.log(2)); process.nextTick(() => console.log(3)); Promise.resolve().then(() => console.log(4));
// 輸出結果 1,3,2,4
注意,只有前一個隊列全部清空以后,才會執行下一個隊列。兩個隊列的概念 nextTickQueue 和微隊列microTaskQueue,也就是說開啟異步任務也分為幾種,像promise對象這種,開啟之后直接進入微隊列中,微隊列內的就是那個任務快就那個先執行完,但是針對于隊列與隊列之間不同的任務,還是會有先后順序,這個先后順序是由隊列決定的。
4.事件循環的階段(idle, prepare忽略了這個階段)事件循環最階段最詳細的講解(官網:https://nodejs.org/en/docs/gu...)
timers階段
次階段包括setTimeout()和setInterval()
IO callbacks
大部分的回調事件,普通的caollback
poll階段
網絡連接,數據獲取,讀取文件等操作
check階段
setImmediate()在這里調用回調
close階段
一些關閉回調,例如socket.on("close", ...)
事件循環注意點
1)Node 開始執行腳本時,會先進行事件循環的初始化,但是這時事件循環還沒有開始,會先 完成下面的事情。
同步任務
發出異步請求
規劃定時器生效的時間
執行process.nextTick()等等
最后,上面這些事情都干完了,事件循環就正式開始了。
2)事件循環同樣運行在單線程環境下,高并發也是依靠事件循環,每產生一個事件,就會加入到該階段對應的隊列中,此時事件循環將該隊列中的事件取出,準備執行之后的callback。
3)假設事件循環現在進入了某個階段,即使這期間有其他隊列中的事件就緒,也會先將當前隊列的全部回調方法執行完畢后,再進入到下一個階段。
5.事件循環中的setTimeOut與setImmediate由于setTimeout在 timers 階段執行,而setImmediate在 check 階段執行。所以,setTimeout會早于setImmediate完成。
setTimeout(() => console.log(1)); setImmediate(() => console.log(2));
上面代碼應該先輸出1,再輸出2,但是實際執行的時候,結果卻是不確定,有時還會先輸出2,再輸出1。
這是因為setTimeout的第二個參數默認為0。但是實際上,Node 做不到0毫秒,最少也需要1毫秒,根據官方文檔,第二個參數的取值范圍在1毫秒到2147483647毫秒之間。也就是說,setTimeout(f, 0)等同于setTimeout(f, 1)。
實際執行的時候,進入事件循環以后,有可能到了1毫秒,也可能還沒到1毫秒,取決于系統當時的狀況。如果沒到1毫秒,那么 timers 階段就會跳過,進入 check 階段,先執行setImmediate的回調函數。
但是,下面的代碼一定是先輸出2,再輸出1。
const fs = require("fs"); fs.readFile("test.js", () => { setTimeout(() => console.log(1)); setImmediate(() => console.log(2)); });
上面代碼會先進入 I/O callbacks 階段,然后是 check 階段,最后才是 timers 階段。因此,setImmediate才會早于setTimeout執行。
6.同步任務中async以及promise的一些誤解問題1:
在面試題中,在同步任務的過程中,不知道大家有沒有疑問,為什么不是執行完async2輸出后執行async1 end輸出,而是接著執行promise1?
引用阮一峰老師書中一句話:“ async 函數返回一個 Promise 對象,當函數執行的時候,一旦遇到 await 就會先返回,等到觸發的異步操作完成,再接著執行函數體內后面的語句。”
簡單的說,先去執行后面的同步任務代碼,執行完成后,也就是表達式中的 Promise 解析完成后繼續執行 async 函數并返回解決結果。(其實還是本輪循環promise的問題,最后的resolve屬于異步,位于本輪循環的末尾。)
問題2:
console.log("promise2")為什么也是在resolve之前執行?
解答:注:此內容來源與阮一峰老師的ES6書籍,調用resolve或者reject并不會終結promise的參數函數的執行。因為立即resolved的Promise是本輪循環的末尾執行,同時總是晚于本輪循環的同步任務。正規的寫法調用resolve或者reject以后,Promise的使命就完成了,后繼操作應該放在then方法后面。所以最好在它的前面加上return語句,這樣就不會出現意外
new Promise((resolve,reject) => { return resolve(1); //后面的語句不會執行 console.log(2); }
問題3:
promise3和script end的執行順序是否有疑問?
解答:因為立即resolved的Promise是本輪循環的末尾執行,同時總是晚于本輪循環的同步任務。 Promise 是一個立即執行函數,但是他的成功(或失敗:reject)的回調函數 resolve 卻是一個異步執行的回調。當執行到 resolve() 時,這個任務會被放入到回調隊列中,等待調用棧有空閑時事件循環再來取走它。本輪循環中最后執行的。
整體結論順序的整體總結就是:
同步任務-> 本輪循環->次輪循環
node.js官網:
事件循環:https://nodejs.org/en/docs/gu...
Timers:https://nodejs.org/dist/lates...
加入我們一起學習吧!
node學習交流群
交流群滿100人不能自動進群, 請添加群助手微信號:【coder_qi】備注node,自動拉你入群。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/106894.html
摘要:小栗子對于可樂來講,只要是同一個品牌的可樂,他們就有著同樣的成分,這被稱之為配方。小栗子對于可樂來說,按照配方把可樂生產出來的過程就是實例化的過程。小栗子類的屬性與正常的變量并無區別。 前言 我是栗子,帶大家從零開始學習Python,希望每篇文章都能讓你收獲滿滿! 今天我們要說的是面向對象的...
必須要看的前言 本文風格:以??簡單易懂??的語言帶你徹底搞懂KNN,了解什么是有監督學習算法。 認真看完這篇文章,徹底了解KNN、了解監督學習算法絕對是一樣很簡單的事情。 注:本篇文章非常詳細,同時我也附加了Python代碼,歡迎收藏后慢慢閱讀。 目錄 必須要看的前言監督學習算法KNN/K近鄰算法1 算法原理1.1 實現過程1.2 距離的確定 2 算法的優缺點3 算法的變種3.1 變...
摘要:整理收藏一些優秀的文章及大佬博客留著慢慢學習原文協作規范中文技術文檔協作規范阮一峰編程風格凹凸實驗室前端代碼規范風格指南這一次,徹底弄懂執行機制一次弄懂徹底解決此類面試問題瀏覽器與的事件循環有何區別筆試題事件循環機制異步編程理解的異步 better-learning 整理收藏一些優秀的文章及大佬博客留著慢慢學習 原文:https://www.ahwgs.cn/youxiuwenzhan...
摘要:如果當前不是主線程則直接調用,如果是線程則創建一個加入到后臺的一個隊列,最終由中的一個線程池去調用。拋出線程狀態非法異常。 while (clazz != null) {String name = clazz.getName();if (name.startsWith(java.) || name.starts...
摘要:解析第題第題為什么的和的中不能做異步操作解析第題第題京東下面代碼中在什么情況下會打印解析第題第題介紹下及其應用。盡量減少操作次數。解析第題第題京東快手周一算法題之兩數之和給定一個整數數組和一個目標值,找出數組中和為目標值的兩個數。 引言 半年時間,幾千人參與,精選大廠前端面試高頻 100 題,這就是「壹題」。 在 2019 年 1 月 21 日這天,「壹題」項目正式開始,在這之后每個工...
閱讀 4233·2021-09-26 10:17
閱讀 877·2021-09-22 15:02
閱讀 3450·2021-09-06 15:00
閱讀 1059·2021-07-25 16:52
閱讀 2740·2019-08-29 16:16
閱讀 2519·2019-08-29 13:25
閱讀 1595·2019-08-26 13:51
閱讀 2191·2019-08-26 10:58