JS的事件循環(huán)執(zhí)行機(jī)制詳解
前言
在前端開發(fā)中,涉及到JS原生的使用原理是非常重要的知識點(diǎn),尤其是在實(shí)際工作過程中會遇到各種復(fù)雜的業(yè)務(wù)需求場景,以及具體開發(fā)中可能會遇到一些涉及基于JS原理的使用,這都要求開發(fā)者能夠很好的了解和掌握J(rèn)S原生的常用原理。JS執(zhí)行是單線程的,它是基于事件循環(huán)的,那么本篇博文就來分享一下關(guān)于JS的事件循環(huán)執(zhí)行機(jī)制,該內(nèi)容不僅在日常前端開發(fā)中是比較重要核心的知識點(diǎn),而且在前端求職面試時候面試官必考的知識點(diǎn),尤其是關(guān)于異步執(zhí)行代碼時候的事件循環(huán),總結(jié)記錄一下,方便后期查閱使用。
JS語言的特點(diǎn)
在分享本篇博文之前,首先再來回顧一下JS的語言特點(diǎn)。眾所周知,JS是單線程的,所有同步任務(wù)都在主線程上執(zhí)行,即執(zhí)行棧 execution context stack,主線程之外還存在一個任務(wù)隊列(也有人稱之為消息隊列)。也就是只能在同一個時間內(nèi)做一件事情,意味著所有任務(wù)都需要排隊執(zhí)行,前面一個任務(wù)結(jié)束之后才會執(zhí)行后面一個任務(wù),JS代碼是從上到下一行一行執(zhí)行的,如果某一行報錯,則停止執(zhí)行下面的代碼;先執(zhí)行同步代碼,再執(zhí)行異步代碼。這樣做會導(dǎo)致最大的問題就是如果執(zhí)行的時間比較長,就會引起頁面渲染不連貫,導(dǎo)致頁面渲染加載阻塞。
JS中同步和異步的使用
為了解決上面說的JS的單線程引起的頁面渲染阻塞問題,結(jié)合多核計算機(jī)的計算能力,HTML5提出了允許JS腳本創(chuàng)建多個線程的操作,至此同步和異步出現(xiàn)了。由于JS是單線程的,瀏覽器在執(zhí)行JS代碼時會先執(zhí)行同步代碼,再執(zhí)行異步代碼。
- 同步:指的就是前一個任務(wù)結(jié)束之后再執(zhí)行后一個任務(wù),程序執(zhí)行的順序與任務(wù)排序的順序是一樣的,保持同步的。
- 同步任務(wù):在主線程上排隊執(zhí)行任務(wù),當(dāng)前一個任務(wù)執(zhí)行完之后,才會執(zhí)行后一個任務(wù)。
- 異步:首先異步與同步是相對的,也就是說異步不按照任務(wù)排序的順序執(zhí)行,程序執(zhí)行的順序與任務(wù)排序的順序是不一致的,也可以理解為異步就是從主線程中發(fā)出一個子線程來完成任務(wù)。
- 異步任務(wù):不用進(jìn)入主線程,直接進(jìn)入任務(wù)隊列的任務(wù),只有任務(wù)隊列通知主線程某個異步任務(wù)可以執(zhí)行的時候,該任務(wù)才會進(jìn)入主線程中執(zhí)行。
JS中常用的四種異步任務(wù)類型有:setTimeout、setlnterval、ES6中的Promise、Ajax異步請求、DOM事件(如:click、resize、onload)。
異步任務(wù)相關(guān)回調(diào)函數(shù)添加到任務(wù)隊列中,即消息隊列。JS中事件循環(huán)(Event Loop)的執(zhí)行機(jī)制,就是采用隊列的存取方式,在執(zhí)行和協(xié)調(diào)各種任務(wù)時,Event Loop 會維護(hù)自己的事件隊列。
事件循環(huán)是什么?
其實(shí)JS的運(yùn)行機(jī)制就是事件循環(huán)。事件循環(huán)(Event Loop) 是讓JS做到既是單線程,又不會阻塞的核心機(jī)制,也是JS并發(fā)模型(Concurrency Model)的基礎(chǔ),是用來協(xié)調(diào)各種事件、用戶交互、腳本執(zhí)行、UI 渲染、網(wǎng)絡(luò)請求等的一種機(jī)制。
下面分享一個簡單的事件循環(huán)示例代碼,具體如下所示:
//代碼語句一 console.log('a'); //代碼語句二 setTimeout(()=>{ ? ? console.log('b'); },1000); //代碼語句三 console.log('c'); //代碼執(zhí)行結(jié)果為: a,c,b
執(zhí)行過程解析:之所以出現(xiàn)上面的輸出結(jié)果,原因就是JS指向代碼是從上往下執(zhí)行,會先執(zhí)行語句一。JS會將語句一放在調(diào)用棧當(dāng)中,然后執(zhí)行代碼在控制臺輸出a,當(dāng)語句一執(zhí)行完畢后,便將其從調(diào)用棧中移出去;接著語句二進(jìn)入調(diào)用棧,語句二會調(diào)用API, 1秒后進(jìn)入回調(diào)隊列,這時候JS將語句二移出調(diào)用棧,繼續(xù)執(zhí)行后面的代碼控制臺輸出了c;這時候進(jìn)入事件循環(huán),它會不斷循環(huán)的訪問回調(diào)隊列,等待1秒后API會將要執(zhí)行的語句二放入回調(diào)隊列,事件循環(huán)將回調(diào)隊列中的內(nèi)容放入調(diào)用棧并執(zhí)行,然后在控制臺輸出b。
事件循環(huán)執(zhí)行過程
事件循環(huán)執(zhí)行過程:先清空調(diào)用棧里面的同步代碼,然后執(zhí)行微任務(wù)隊列中的微任務(wù),接著DOM渲染,觸發(fā)事件循環(huán)反復(fù)輪詢回調(diào)隊列中是否有需要執(zhí)行的代碼語句,如果有就放入調(diào)用棧中繼續(xù)執(zhí)行。
- 同步的代碼:調(diào)用棧執(zhí)行后直接出棧;
- 異步的代碼:放到API中,等待合適的時機(jī)放入到回調(diào)隊列,等??臻e的時候事件循環(huán)開始工作,進(jìn)行輪詢。
注意:微任務(wù)比宏任務(wù)執(zhí)行的時機(jī)要早,即微任務(wù)會在DOM渲染前觸發(fā),宏任務(wù)則在DOM渲染后觸發(fā)。
舉一個簡單的示例,具體代碼如下所示:
// 語句一 console.log('a'); // 語句二 setTimeout(()=>{ ? ? console.log('b'); },0); //語句三 Promise.resolve().then(()=>{ ? ? console.log('c'); }) // 語句四 console.log('d'); //代碼執(zhí)行結(jié)果為:a,d,c,b
微任務(wù)和宏任務(wù)的區(qū)別
JS 中規(guī)定任務(wù)為兩類:一類是宏任務(wù)(macro task),一類是微任務(wù)(micro task),并且每個宏任務(wù)結(jié)束后,都要清空所有微任務(wù)。
- 宏任務(wù):當(dāng)前調(diào)用棧中執(zhí)行的代碼成為宏任務(wù),是由瀏覽器規(guī)定的,宏任務(wù)隊列由事件觸發(fā)線程維護(hù)。屬于宏任務(wù)的有:主代碼塊、定時器等。具體的宏任務(wù)如下所示:
setTimeout、setInterval、Ajax、DOM事件。
- 微任務(wù):當(dāng)前宏任務(wù)執(zhí)行完,在下一個宏任務(wù)開始之前需要執(zhí)行的任務(wù)就叫微任務(wù),是由ES6語法規(guī)定的,微任務(wù)隊列是由JS引擎線程維護(hù)。屬于微任務(wù)的有:promise.then,proness.nextTick 等。具體的微任務(wù)如下所示:
Promise、async、await。
JS執(zhí)行/運(yùn)行機(jī)制
JS的執(zhí)行/運(yùn)行機(jī)制,具體如下所示:
1、在執(zhí)行棧中執(zhí)行一個宏任務(wù);
2、執(zhí)行過程中遇到微任務(wù),將微任務(wù)添加到微任務(wù)隊列中;
3、當(dāng)前宏任務(wù)執(zhí)行完畢,立即執(zhí)行微任務(wù)隊列中的任務(wù);
4、當(dāng)前微任務(wù)隊列中的任務(wù)執(zhí)行完畢,檢查渲染,GUI線程接管渲染;
5、渲染完畢后,js線程接管,開啟下一次事件循環(huán),執(zhí)行下一次宏任務(wù)(事件隊列中?。?/p>
最后
通過本文關(guān)于前端開發(fā)中關(guān)于JS的事件循環(huán)執(zhí)行機(jī)制的詳細(xì)介紹,事件循環(huán)執(zhí)行機(jī)制不管是在實(shí)際的前端開發(fā)工作中還是在前端求職面試中都是非常關(guān)鍵的知識點(diǎn),所以作為前端開發(fā)者來說必須要掌握它相關(guān)的內(nèi)容,尤其是從事前端開發(fā)不久的開發(fā)者來說尤為重要,是一篇值得閱讀的文章,重要性就不在贅述。
以上就是JS的事件循環(huán)執(zhí)行機(jī)制詳解的詳細(xì)內(nèi)容,更多關(guān)于JS事件循環(huán)執(zhí)行機(jī)制的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
解決layui的table插件無法多層級獲取json數(shù)據(jù)的問題
今天小編就為大家分享一篇解決layui的table插件無法多層級獲取json數(shù)據(jù)的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-09-09Javascript實(shí)現(xiàn)從小到大的數(shù)組轉(zhuǎn)換成二叉搜索樹
這篇文章主要介紹了Javascript實(shí)現(xiàn)從小到大的數(shù)組轉(zhuǎn)換成二叉搜索樹的相關(guān)資料,需要的朋友可以參考下2017-06-06原生ajax處理json格式數(shù)據(jù)的實(shí)例代碼
這篇文章主要介紹了原生ajax處理json格式數(shù)據(jù)的實(shí)例代碼,需要的朋友可以參考下2016-12-12JavaScript對數(shù)字的判斷與處理實(shí)例分析
這篇文章主要介紹了JavaScript對數(shù)字的判斷與處理方法,實(shí)例分析了javascript判斷數(shù)字的常見方法與針對數(shù)字處理的技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-02-02基于bootstrap實(shí)現(xiàn)多個下拉框同時搜索功能
這篇文章主要為大家詳細(xì)介紹了基于bootstrap實(shí)現(xiàn)多個下拉框同時搜索功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-07-07微信小程序?qū)崿F(xiàn)獲取小程序碼和二維碼java接口開發(fā)
這篇文章主要介紹了微信小程序?qū)崿F(xiàn)獲取小程序碼和二維碼java接口開發(fā),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-03-03JS實(shí)現(xiàn)三級折疊菜單特效,其它級可自動收縮
這篇文章主要介紹了JS實(shí)現(xiàn)三級折疊菜單特效,其它級可自動收縮,需要的朋友可以參考下2015-08-08underscore之Chaining_動力節(jié)點(diǎn)Java學(xué)院整理
本文通過文字說明與代碼的形式給大家介紹了underscore之Chaining的相關(guān)知識,感興趣的朋友一起學(xué)習(xí)吧2017-07-07