Javascript運(yùn)行機(jī)制之Event Loop
一、四個概念
1、Javascript是單線程
單線程意味著我們的js
代碼只能從上往下同步執(zhí)行,同一時間只能執(zhí)行一個任務(wù),這會導(dǎo)致某些執(zhí)行時間較長或者執(zhí)行時間不確定的任務(wù)會卡住其它任務(wù)的正常執(zhí)行,Event Loop
出現(xiàn)的原因正是為了解決此問題。
2、任務(wù)隊(duì)列
為了解決上述的排隊(duì)問題,有了任務(wù)隊(duì)列,瀏覽器在異步任務(wù)有了結(jié)果后,將其添加到任務(wù)隊(duì)列,以待將來執(zhí)行,其他任務(wù)就在主線程上同步執(zhí)行。
這里要注意的是,向任務(wù)隊(duì)列中添加任務(wù)的時機(jī)是異步任務(wù)有結(jié)果后。其實(shí)任務(wù)隊(duì)列中存在的就是異步任務(wù)的回調(diào)函數(shù)。
3、同步任務(wù)、異步任務(wù)
Js程序中的同步任務(wù)是指在主線程中執(zhí)行的任務(wù),異步任務(wù)是指進(jìn)入任務(wù)隊(duì)列中的任務(wù)
4、Javascript執(zhí)行棧
所有的同步任務(wù)都在主線程上執(zhí)行,行成一個執(zhí)行棧。當(dāng)主線程上任務(wù)執(zhí)行完畢后,從任務(wù)隊(duì)列中取出任務(wù)執(zhí)行。
var name = "zhouwei"; setTimeout(() => { console.log(1); }, 1000); console.log(name);
上面代碼在瀏覽器中執(zhí)行如下,我們將程序全局執(zhí)行環(huán)境的代碼理解為包裹在一個main函數(shù)中的代碼,這段代碼的執(zhí)行棧變化如下圖:
- 開始執(zhí)行代碼,將
main
任務(wù)(全局代碼入棧執(zhí)行),當(dāng)遇到異步任務(wù)(setTimeout
后)。 - 瀏覽器接管異步任務(wù),并在1s后將異步任務(wù)的結(jié)果(回調(diào)函數(shù))添加到任務(wù)隊(duì)列。
- 執(zhí)行棧中的同步任務(wù)執(zhí)行完畢,此時任務(wù)隊(duì)列為空(未到1s),執(zhí)行棧也為空
- 異步任務(wù)有結(jié)果后,首先進(jìn)入任務(wù)隊(duì)列排隊(duì)(因?yàn)榭赡苡泻芏喈惒饺蝿?wù))。
- 執(zhí)行棧從任務(wù)隊(duì)列中取出任務(wù)開始同步執(zhí)行。
- 重復(fù)執(zhí)行第5步。
二、Event Loop
Js執(zhí)行棧不斷的從任務(wù)隊(duì)列中讀取任務(wù)并執(zhí)行的過程就是Event Loop
我們知道任務(wù)隊(duì)列中存放的是異步任務(wù)的結(jié)果,那么異步任務(wù)都有哪些了?
- 1、事件
Javascript
中的事件有很多,都是屬于異步任務(wù)。由瀏覽器接管,當(dāng)事件觸發(fā)時,將事件的回調(diào)加入的任務(wù)隊(duì)列中,在Js執(zhí)行棧中沒有任務(wù)時執(zhí)行。
- 2、Http請求
- 3、定時器
- 4、requestAnimationFrame等
宏任務(wù)(macrotask
)和微任務(wù)(microtask
)
在了解了任務(wù)隊(duì)列和Event Loop
后,我們知道了Js執(zhí)行棧從任務(wù)隊(duì)列中讀取任務(wù)執(zhí)行,但這個具體工程我們?nèi)蝿?wù)不清楚,這里引出了宏任務(wù)和微任務(wù)的的概念,幫助我們理解Event Loop。
進(jìn)入任務(wù)隊(duì)列中的異步任務(wù)回調(diào)分為了宏任務(wù)和微任務(wù), Js執(zhí)行棧執(zhí)行宏任務(wù)和微任務(wù)的規(guī)則如下圖所示。
Js執(zhí)行棧首先執(zhí)行一個宏任務(wù)(全局代碼) -> 從任務(wù)隊(duì)列中讀取所有微任務(wù)執(zhí)行 -> UI rendering(瀏覽器渲染界面) -> 從任務(wù)隊(duì)列讀取一個宏任務(wù) -> 所有微任務(wù) -> UI rendering -> …
在每一輪的Event Loop結(jié)束后(1個宏任務(wù) + 所有微任務(wù)),瀏覽器開始渲染界面(如果有需要渲染的UI,否則不執(zhí)行UI rendering),在
UI rendering
結(jié)束后,開始下一輪Event Loop。
哪些是宏任務(wù)?
- setTimeout
- setInterval
- setImmediate (Node)
- requestAnimationFrame (瀏覽器)
- I/O (事件回調(diào))
- UI rendering (瀏覽器渲染)
哪些是微任務(wù)?
- Promise
- process.nextTick (Node)
- MutationObserver (現(xiàn)代瀏覽器提供的用來檢測 DOM 變化的網(wǎng)頁接口)
setTimeout延時問題
一般來說在代碼中setTimeout
中回調(diào)的執(zhí)行時間都是大于設(shè)置的時間。 這是因?yàn)樵?code>setTimeout指定時間到達(dá)后,雖然回調(diào)函數(shù)被添加到了任務(wù)隊(duì)列,但是此時Js執(zhí)行棧中可能有正在執(zhí)行的任務(wù),此回調(diào)需要等待Js執(zhí)行棧的任務(wù)執(zhí)行完畢后才有機(jī)會執(zhí)行,這就是setTimeout
延時問題。
三、實(shí)戰(zhàn)
練習(xí)下下方代碼輸出結(jié)果吧:
console.log(1); setTimeout(() => { console.log(2); Promise.resolve().then(() => { console.log(3) }); }); new Promise(resolve => { console.log(4); setTimeout(() => { console.log(5); }); resolve(6) }).then(data => { console.log(data); }) setTimeout(() => { console.log(7); }) console.log(8);
用上方的我們說過的js執(zhí)行機(jī)制來分析這道題:
1: 執(zhí)行全局任務(wù)中的同步代碼輸出:
1
4
8
這里需要注意的是Promise
接受的handle
函數(shù)是同步任務(wù),而then
方法是異步任務(wù),所以會直接輸出4。
2: 這時的任務(wù)隊(duì)列中有三個setTimeout
的宏任務(wù),和一個Promise
的微任務(wù)
// 此時的宏任務(wù)是 setTimeout(() => { console.log(2); Promise.resolve().then(() => { console.log(3) }); }); setTimeout(() => { console.log(5); }); setTimeout(() => { console.log(7); }) // 此時微任務(wù)是 then(data => { console.log(data); })
執(zhí)行一個微任務(wù), 輸出:6
3: 接著執(zhí)行第一個宏任務(wù)
setTimeout(() => { console.log(2); Promise.resolve().then(() => { console.log(3) }); });
輸出:2
在此宏任務(wù)中,向任務(wù) 隊(duì)列添加了一個微任務(wù)。此時任務(wù)隊(duì)列有了新的微任務(wù)。
4:執(zhí)行一個微任務(wù),輸出:3
then(() => { console.log(3) });
5: 繼續(xù)按照規(guī)則執(zhí)行任務(wù), 輸出: 5、7
整體輸出情況是:
1、4、8、6、2、3、5、7
你的答案是不是這樣呢?
總結(jié):
javascritp
的任務(wù)分為同步任務(wù)和異步任務(wù)- 同步任務(wù)在主線程(Js執(zhí)行棧)中執(zhí)行,異步任務(wù)被其他線程接管,并在異步任務(wù)有結(jié)果后,將其回調(diào)添加到任務(wù)隊(duì)列。
- 任務(wù)隊(duì)列中的任務(wù)分為了宏任務(wù)和微任務(wù)。Js執(zhí)行??偸窍葓?zhí)行一個宏任務(wù),再執(zhí)行完所有微任務(wù)…
到此這篇關(guān)于Javascript
運(yùn)行機(jī)制之Event Loop的文章就介紹到這了,更多相關(guān)Javascript運(yùn)行機(jī)制Event Loop內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
js實(shí)現(xiàn)QQ面板拖拽效果(慕課網(wǎng)DOM事件探秘)(全)
這篇文章主要為大家詳細(xì)介紹了QQ面板拖拽效果,探秘慕課網(wǎng)DOM事件,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-09-09JavaScript?TWaver使用中間點(diǎn)畫折線的方法
這篇文章主要介紹了JavaScript?TWaver使用中間點(diǎn)畫折線的方法,TWaver的圖形組件庫中提供了拓?fù)浣M件、地圖組件、設(shè)備圖組件,以及表格、樹圖、屬性表、圖表等豐富的通用圖形界面組件2022-07-07JavaScript變量聲明var,let.const及區(qū)別淺析
這篇文章主要介紹了JavaScript變量聲明var,let.const及區(qū)別淺析,需要的朋友可以參考下2018-04-04js實(shí)現(xiàn)文字超出部分用省略號代替實(shí)例代碼
關(guān)于超出一定字?jǐn)?shù)用省略號顯示的問題,這種要求在我們?nèi)粘i_發(fā)的時候經(jīng)常見到,我們之前基本都是用CSS來完成的,今天給大家分享個Javascript實(shí)現(xiàn)這個功能的示例代碼,有需要的可以參考借鑒。2016-09-09