摘要:主線程不斷重復上面的三步,此過程也就是常說的事件循環。所以主線程代碼執行時間過長,會阻塞事件循環的執行。參考資料這一次,徹底弄懂執行機制任務隊列的順序機制事件循環搞懂異步事件輪詢與中的事件循環
1. 說明
讀過本文章后,您能知道:
JavaScript代碼在瀏覽器中的執行機制和事件循環
面試中經常遇到的代碼輸出順序問題
首先通過一段代碼來驗證你是否了解代碼輸出順序,如果你不知道輸出順序,那么本文可以幫助你了解:
console.log(1) setTimeout(function () { new Promise(function (resolve) { console.log(2) resolve() }) .then(() => { console.log(3) }) }) setTimeout(function () { console.log(4) }) console.log(5)2. JavaScript執行機制
JavaScript語言的執行是單線程(single thread)的。
所謂的單線程,就是指一次只執行一個任務,如果有多個任務,就必須排隊,前面一個任務完成,才能執行后面任務。
這種模式的好處是實現起來比較簡單,執行環境相對單純;壞處是只要有一個任務耗時很長,后面的任務都必須排隊等待,會拖延整個程序的執行。常見的瀏覽器無響應(假死),往往就是因為某一段JavaScript代碼長時間運行(比如死循環),導致整個頁面卡在這個地方,其他任務無法執行。
為了解決這個問題,JavaScript語言將任務的執行模式分成兩種:同步(Synchronous)和異步(Asynchronous)。
同步任務:在主線程上排隊執行的任務,只有前一個任務執行完畢,才能執行后一個任務。
異步任務:不進入主線程、而進入"任務隊列"(task queue)的任務,只有"任務隊列"通知主線程,某個異步任務可以執行了,該任務才會進入主線程執行。
JavaScript執行機制:
1、所有同步任務都在主線程上執行,形成一個執行棧(execution context stack)。3. JavaScript執行機制中名詞介紹 3.1 執行棧
2、主線程之外,還存在一個"任務隊列"(task queue)。只要異步任務有了運行結果,就在"任務隊列"之中放置一個事件。
3、一旦"執行棧"中的所有同步任務執行完畢,系統就會讀取"任務隊列",如果有有執行任務,則進入執行棧,開始執行。
4、主線程不斷重復上面的三步,此過程也就是常說的Event Loop(事件循環)。
當我們調用一個方法的時候,js會生成一個與這個方法相對應的執行環境,也叫執行上下文,這個執行環境存在著這個方法的私有作用域、參數、this對象等等。因為js是單線程的,同一時間只能執行一個方法,所以當一系列的方法被依次調用的時候,js會先解析這些方法,把其中的同步任務按照執行順序排隊到一個地方,這個地方叫做執行棧。
3.2 主線程JavaScript是單線程的,那么這個單線程就成為主線程。而事件循環在主線程執行完執行棧代碼后,才執行的。所以主線程代碼執行時間過長,會阻塞事件循環的執行。只有當執行棧為空的時候(同步代碼執行完畢),才會執行事件循環來觀察有哪些事件回調需要執行,當事件循環檢測到任務隊列有事件就讀取出回調放到執行棧由主線程執行。
3.3 任務隊列任務隊列也有時稱叫消息隊列、回調隊列。
異步操作會將相關回調添加到任務隊列中。而不同的異步操作添加到任務隊列的時機也不同,如onclick, setTimeout,ajax處理的方式都不同,這些異步操作是由瀏覽器內核的webcore來執行的,webcore包含下圖中的3種 webAPI,分別是DOM Binding、network、timer模塊。
DOM Binding 模塊處理一些DOM綁定事件,如onclick事件觸發時,回調函數會立即被webcore添加到任務隊列中。
network 模塊處理Ajax請求,在網絡請求返回時,才會將對應的回調函數添加到任務隊列中。
timer 模塊會對setTimeout等計時器進行延時處理,當時間到達的時候,才會將回調函數添加到任務隊列中。
3.4 事件循環如上圖所示,JavaScript整體執行過程:
主線程運行的時候會生成堆(heap)和棧(stack);
js從上到下解析方法,將其中的同步任務按照執行順序排列到執行棧中;
當程序調用外部的API時,比如ajax、setTimeout等,會將此類異步任務掛起,繼續執行執行棧中的任務,等異步任務返回結果后,再按照執行順序排列到任務隊列中;
主線程先將執行棧中的同步任務清空,然后檢查任務隊列中是否有任務,如果有,就將第一個事件對應的回調推到執行棧中執行,若在執行過程中遇到異步任務,則繼續將這個異步任務排列到任務隊列中。
主線程每次將執行棧清空后,就去任務隊列中檢查是否有任務,如果有,就每次取出一個推到執行棧中執行,這個過程是循環往復的... ...,這個過程被稱為“Event Loop 事件循環”。
也可以參考如下鏈接:https://html.spec.whatwg.org/...
4. 宏任務和微任務出現Promise后,JavaScript對于任務的定義除了廣義的同步任務和異步任務,又對任務做了更精細的定義,macrotask(宏任務)和 microtask(微任務):
macrotask(按優先級順序排列): script(你的全部JS代碼,“同步代碼”), setTimeout, setInterval, setImmediate(node的), I/O,UI rendering
microtask(按優先級順序排列):process.nextTick(node的),Promise(這里指瀏覽器原生實現的 Promise), Object.observe, MutationObserver
注意:宏任務、微任務中出現的nodejs中的方法是nodejs專有的,瀏覽器的JavaScript環境暫時沒有支持。
4.1 事件循環對宏任務和微任務的處理有了宏任務和微任務后,JavaScript事件循環對此處理方法如下形式:
js引擎首先從macrotask queue中取出第一個任務,執行完畢后,將microtask queue中的所有任務取出,按順序全部執行;
然后再從macrotask queue(宏任務隊列)中取下一個,執行完畢后,再次將microtask queue(微任務隊列)中的全部取出;
循環往復,直到兩個queue中的任務都取完。
注意::此處把執行同步代碼算成第一個宏任務了。
5. 一個實際例子講解JavaScript執行流程如下代碼:
console.log("1"); setTimeout(function() { console.log("2"); new Promise(function(resolve) { console.log("3"); resolve(); }).then(function() { console.log("4") }) }) console.log("5"); setTimeout(function() { console.log("6"); new Promise(function(resolve) { console.log("7"); resolve(); }).then(function() { console.log("8") }) }) console.log("9");
上面代碼執行過程:
第一輪事件循環
整體script代碼(同步代碼)作為第一個宏任務進入主線程,遇到console.log,輸出1
遇到setTimeout,其回調函數被放到宏任務隊列中,暫記為setTmineout1
遇到console.log,輸出5
遇到setTimeout,其回調函數被放到宏任務隊列中,暫記為setTmineout2
遇到console.log,輸出9
一個宏任務執行結束,去微任務隊列查找是否有待執行的任務,沒有,結束
第一輪循環結束,輸出:1 5 9
第二輪事件循環
從宏任務隊列中取出一個任務,即setTmineout1,開始執行
遇到console.log,輸出2
遇到Promise,創建Promise,輸出了3,同時把Promise.then回調函數放到微任務隊列
一個宏任務執行結束,去微任務隊列查找是否有待執行的任務, 發現有微任務,全部取出放到執行棧執行
執行微任務,此時就一個微任務,console.log,輸出4
微任務執行結束
第二輪循環結束,輸出:2 4
第三輪事件循環與第二輪一樣,輸出:6 7 8
事件循環發現所有任務都已經處理完畢,此時程序執行結束
全部的輸出:1 5 9 2 3 4 6 7 8,可復制代碼到chrome瀏覽器控制臺中運行校驗結果
注意:瀏覽器環境JavaScript的執行機制和node中JavaScript的執行機制是不同的。
參考資料這一次,徹底弄懂 JavaScript 執行機制
JavaScript任務隊列的順序機制(事件循環)
搞懂 JavsScript 異步 —? 事件輪詢
JS與Node.js中的事件循環
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/101012.html
摘要:從異步過程的角度看,函數就是異步過程的發起函數,事件監聽函數就是異步過程的回調函數。事件觸發時,表示異步任務完成,會將事件監聽器函數封裝成一條消息放到消息隊列中,等待主線程執行。 1.為什么JavaScript是單線程? JavaScript語言的一大特點就是單線程,也就是說,同一個時間只能做一件事。那么,為什么JavaScript不能有多個線程呢?這樣能提高效率啊。JavaScrip...
摘要:的單線程,與它的用途有關。特點的顯著特點異步機制事件驅動。隊列的讀取輪詢線程,事件的消費者,的主角。它將不同的任務分配給不同的線程,形成一個事件循環,以異步的方式將任務的執行結果返回給引擎。 這兩天跟同事同事討論遇到的一個問題,js中的event loop,引出了chrome與node中運行具有setTimeout和Promise的程序時候執行結果不一樣的問題,從而引出了Nodejs的...
摘要:的單線程,與它的用途有關。事件循環事件循環是指主線程重復從消息隊列中取消息執行的過程。到此為止,就完成了工作線程對主線程的通知,回調函數也就得到了執行。 一. 區分進程和線程 很多新手是區分不清線程和進程的,沒有關系。這很正常。先看看下面這個形象的比喻: 進程是一個工廠,工廠有它的獨立資源-工廠之間相互獨立-線程是工廠中的工人,多個工人協作完成任務-工廠內有一個或多個工人-工人之間共享...
摘要:異步任務必須指定回調函數,當異步任務從任務隊列回到執行棧,回調函數就會執行。事件循環主線程從任務隊列中讀取事件,這個過程是循環不斷的,所以整個的這種運行機制又稱為。事件循環事件循環是指主線程重復從消息隊列中取消息執行的過程。 參考鏈接:這一次,徹底弄懂 JavaScript 執行機制https://zhuanlan.zhihu.com/p/...從瀏覽器多進程到JS單線程,JS運行機制...
摘要:事件循環事件循環是實現異步的一種方法,也是的執行機制。最后的最后是一門單線程語言是的執行機制部分內容轉自 1.單線程 javascript是一門單線程語言 2.javascript事件循環 同步任務 異步任務 showImg(https://segmentfault.com/img/bVbufUd?w=1268&h=1062);除了廣義的同步任務和異步任務,我們對任務有更精細的定義...
閱讀 1613·2021-11-23 09:51
閱讀 1184·2019-08-30 13:57
閱讀 2264·2019-08-29 13:12
閱讀 2018·2019-08-26 13:57
閱讀 1202·2019-08-26 11:32
閱讀 981·2019-08-23 15:08
閱讀 708·2019-08-23 14:42
閱讀 3087·2019-08-23 11:41