摘要:更多文章請前往我的個人博客這個問題是有關執行順序和的。其中,整體代碼,可以理解為待執行的所有代碼。當隊列執行完后再執行一個任務。然后再次回到新的事件循環。所以兩個執行完后隊列里只剩下第一個里的。
『前端碎碎念』系列會記錄我平時看書或者看文章遇到的問題,一般都是比較基礎但是容易遺忘的知識點,你也可能會在面試中碰到。 我會查閱一些資料并可能加上自己的理解,來記錄這些問題。更多文章請前往我的個人博客
這個問題是有關執行順序和Event Loop的。關于Event Loop和任務隊列等概念,可以先閱讀我引用中的文章,本文主要分析一些存在的疑惑點。
下面這個例子比較典型:
setImmediate(function(){ console.log(1); },0); setTimeout(function(){ console.log(2); },0); new Promise(function(resolve){ console.log(3); resolve(); console.log(4); }).then(function(){ console.log(5); }); console.log(6); process.nextTick(function(){ console.log(7); }); console.log(8); //輸出結果是3 4 6 8 7 5 2 1
在解釋輸出結果之前,我們來看幾個概念:
macro-task: script (整體代碼),setTimeout, setInterval, setImmediate, I/O, UI rendering.
micro-task: process.nextTick, Promise(原生),Object.observe,MutationObserver
除了script整體代碼,micro-task的任務優先級高于macro-task的任務優先級。
其中,script(整體代碼) ,可以理解為待執行的所有代碼。
所以執行順序如下:
第一步. script整體代碼被執行,執行過程為
創建setImmediate macro-task
創建setTimeout macro-task
創建micro-task Promise.then 的回調,并執行script console.log(3); resolve(); console.log(4); 此時輸出3和4,雖然resolve調用了,執行了但是整體代碼還沒執行完,無法進入Promise.then 流程。
console.log(6)輸出6
process.nextTick 創建micro-task
console.log(8) 輸出8
第一個過程過后,已經輸出了3 4 6 8
第二步. 由于其他micro-task 的 優先級高于macro-task。
此時micro-task 中有兩個任務按照優先級process.nextTick 高于 Promise。
所以先輸出7,再輸出5
第三步,micro-task 任務列表已經執行完畢,家下來執行macro-task. 由于setTimeout的優先級高于setIImmediate,所以先輸出2,再輸出1。
整個過程描述起來像是同步操作,實際上是基于Event Loop的事件循環。
關于micro-task和macro-task的執行順序,可看下面這個例子(來自《深入淺出Node.js》):
//加入兩個nextTick的回調函數 process.nextTick(function () { console.log("nextTick延遲執行1"); }); process.nextTick(function () { console.log("nextTick延遲執行2"); }); // 加入兩個setImmediate()的回調函數 setImmediate(function () { console.log("setImmediate延遲執行1"); // 進入下次循環 process.nextTick(function () { console.log("強勢插入"); }); }); setImmediate(function () { console.log("setImmediate延遲執行2"); }); console.log("正常執行");
書中給出的執行結果是:
正常執行 nextTick延遲執行1 nextTick延遲執行2 setImmediate延遲執行1 強勢插入 setImmediate延遲執行2
process.nextTick在兩個setImmediate之間強行插入了。
但運行這段代碼發現結果卻是這樣:
正常執行 nextTick延遲執行1 nextTick延遲執行2 setImmediate延遲執行1 setImmediate延遲執行2 強勢插入
樸老師寫那本書的時候,node最新版本為0.10.13,而我的版本是6.x
老版本的Node會優先執行process.nextTick。
當process.nextTick隊列執行完后再執行一個setImmediate任務。然后再次回到新的事件循環。所以執行完第一個setImmediate后,隊列里只剩下第一個setImmediate里的process.nextTick和第二個setImmediate。所以process.nextTick會先執行。
而在新版的Node中,process.nextTick執行完后,會循環遍歷setImmediate,將setImmediate都執行完畢后再跳出循環。所以兩個setImmediate執行完后隊列里只剩下第一個setImmediate里的process.nextTick。最后輸出"強勢插入"。
具體實現可參考Node.js源碼。
關于優先級的另一個比較清晰的版本:
觀察者優先級
在每次輪訓檢查中,各觀察者的優先級分別是:
idle觀察者 > I/O觀察者 > check觀察者。
idle觀察者:process.nextTick
I/O觀察者:一般性的I/O回調,如網絡,文件,數據庫I/O等
check觀察者:setImmediate,setTimeout
setImmediate 和 setTimeout 的優先級
看下面這個例子:
setImmediate(function () { console.log("1"); }); setTimeout(function () { console.log("2"); }, 0); console.log("3"); //輸出結果是3 2 1
我們知道現在HTML5規定setTimeout的最小間隔時間是4ms,也就是說0實際上也會別默認設置為最小值4ms。我們把這個延遲加大
上面說到setTimeout 的優先級比 setImmediate的高,其實這種說法是有條件的。
再看下面這個例子,為setTimeout增加了一個延遲20ms的時間:
setImmediate(function () { console.log("1"); }); setTimeout(function () { console.log("2"); }, 20); console.log("3"); //輸出結果是3 2 1
setTimeout延遲20ms再執行,而setImmediate是立即執行,竟然2比1還先輸出??
試試打印出這個程序的執行時間:
var t1 = +new Date(); setImmediate(function () { console.log("1"); }); setTimeout(function () { console.log("2"); },20); console.log("3"); var t2 = +new Date(); console.log("time: " + (t2 - t1)); //輸出 3 time: 23 2 1
程序執行用了23ms, 也就是說,在script(整體代碼)執行完之前,setTimeout已經過時了,所以當進入macro-task的時候setTimeout依然優先于setImmediate執行。如果我們把這個值調大一點呢?
var t1 = +new Date(); setImmediate(function () { console.log("1"); }); setTimeout(function () { console.log("2"); },30); console.log("3"); var t2 = +new Date(); console.log("time: " + (t2 - t1)); //輸出 3 time: 23 1 2
setImmediate早于setTimeout執行了,因為進入macro-task 循環的時候,setTimeout的定時器還沒到。
以上實驗是基于6.6.0版本Node.js測試,實際上在碰到類似這種問題的時候,最好的辦法是參考標準,并查閱源碼,不能死記概念和順序,因為標準也是會變的。包括此文也是自學總結,經供參考。
參考:
https://www.zhihu.com/questio...
https://segmentfault.com/a/11...
http://www.jianshu.com/p/837b...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/86791.html
摘要:異步在中,是在單線程中執行的沒錯,但是內部完成工作的另有線程池,使用一個主進程和多個線程來模擬異步。在事件循環中,觀察者會不斷的找到線程池中已經完成的請求對象,從中取出回調函數和數據并執行。 1. 介紹 單線程編程會因阻塞I/O導致硬件資源得不到更優的使用。多線程編程也因為編程中的死鎖、狀態同步等問題讓開發人員頭痛。Node在兩者之間給出了它的解決方案:利用單線程,遠離多線程死鎖、狀態...
摘要:二瀏覽器端在講解事件循環之前先談談中同步代碼異步代碼的執行流程。三端我自己認為的事件循環和瀏覽器端還是有點區別的,它的事件循環依靠引擎。四總結本篇主要介紹了瀏覽器和對于事件循環機制實現,由于能力水平有限,其中可能有誤之處歡迎指出。 一、前言 前幾天聽公司一個公司三年的前端說今天又學到了一個知識點-微任務、宏任務,我問他這是什么東西,由于在吃飯他淺淺的說了下,當時沒太理解就私下學習整理一...
摘要:前言以異步和事件驅動的特性著稱但異步是怎么實現的呢其中核心的一部分就是下文中內容基本來自于文檔有不準確地方請指出什么是能讓的操作表現得無阻塞盡管是單線程的但通過盡可能的將操作放到操作系統內核由于現在大多數內核都是多線程的它們可以在后臺執行多 前言 Node.js以異步I/O和事件驅動的特性著稱,但異步I/O是怎么實現的呢?其中核心的一部分就是event loop,下文中內容基本來自于N...
摘要:瀏覽器與的異同,以及部分機制有人對部分迷惑,本身構造函數是同步的,是異步。瀏覽器的的已全部分析完成,過程中引用阮一峰博客,知乎,部分文章內容,侵刪。 瀏覽器與NodeJS的EventLoop異同,以及部分機制 PS:有人對promise部分迷惑,Promise本身構造函數是同步的,.then是異步。---- 2018/7/6 22:35修改 javascript 是一門單線程的腳本...
閱讀 2594·2021-11-17 09:33
閱讀 3935·2021-10-19 11:46
閱讀 909·2021-10-14 09:42
閱讀 2251·2021-09-22 15:41
閱讀 4204·2021-09-22 15:20
閱讀 4627·2021-09-07 10:22
閱讀 2301·2021-09-04 16:40
閱讀 810·2019-08-30 15:52