javascript中的event loop事件循環(huán)詳解
前言
javascript是單線程的語言,也就是說,同一個時間只能做一件事。而這個單線程的特性,與它的用途有關(guān),作為瀏覽器腳本語言,JavaScript的主要用途是與用戶互動,以及操作DOM。這決定了它只能是單線程,否則會帶來很復(fù)雜的同步問題。比如,假定JavaScript同時有兩個線程,一個線程在某個DOM節(jié)點上添加內(nèi)容,另一個線程刪除了這個節(jié)點,這時瀏覽器應(yīng)該以哪個線程為準(zhǔn)?
為了利用多核CPU的計算能力,HTML5提出Web Worker標(biāo)準(zhǔn),允許JavaScript腳本創(chuàng)建多個線程,但是子線程完全受主線程控制,且不得操作DOM。所以,這個新標(biāo)準(zhǔn)并沒有改變JavaScript單線程的本質(zhì)
這是今天一個朋友發(fā)給我的一個面試題,
感覺還挺有意思的,
寫個博客以供分享
先看看這個面試題目:
觀察下面的代碼,寫出輸出結(jié)果
console.log('0')
setTimeout(function () {
console.log('1');
});
new Promise(function(resolve,reject){
console.log('2')
resolve(3)
}).then(function(val){
console.log(val)
})
console.log(4)
輸出結(jié)果: “0” “2” 4 3 “1”
今天主要是分析為什么輸出結(jié)果是這樣的?這就和 javascript 的執(zhí)行機制密切相關(guān)了.
Event Queue 和 Event Loop
javascript 是一門單線程的語言, 這就意味著在執(zhí)行代碼的時候, 都只有一個主線程來處理所有的任務(wù).
我們都知道 javascript 包括同步代碼和異步代碼, 那么 javascript 是怎么處理這兩種情況的呢?
- 同步和異步任務(wù)分別進入不同的執(zhí)行 場所, 同步的進入主線程,異步的進入 Event Table 并注冊函數(shù)
- 當(dāng)指定的事情完成時, Event Table 會將這個函數(shù)(回調(diào)函數(shù))移入 Event Queue
- 主線程內(nèi)的任務(wù)執(zhí)行完畢為空, 會去 Event Queue 讀取對應(yīng)的函數(shù),進入主線程執(zhí)行
- 上述過程會不斷重復(fù), 也就是常說的 Event Loop(事件循環(huán))
這里我們引進了 Event Queue 事件隊列這一概念. 所有異步操作的回調(diào)都會進入到這里. 然后等到主線程空閑, 就會從這里調(diào)取回調(diào)執(zhí)行.
setTimeout
setTimeout 相信大家都有使用過, 可以延時執(zhí)行并且是異步執(zhí)行的.
但是有時候我們得到的結(jié)果往往是代碼實際執(zhí)行的時間比我們想要延時執(zhí)行的時間要久。這又是為什么呢?
這就和我們之前所說的 Event Loop 有關(guān)了, 我們可以來具體看下 setTimeout 的執(zhí)行步驟:
setTimeout(function () {
asyncFn()
}, 1000);
syncFn()
- asyncFn 將異步執(zhí)行函數(shù)放在 Event Table, 并且開始計時
- 開始執(zhí)行 syncFn, 但是 syncFn 可能需要處理的內(nèi)容很多, 執(zhí)行時間超過 1 秒, 但是計時還在繼續(xù)
- 計時到達 1 秒, setTimeout 延時完成, asyncFn 進入 Event Queue 事件隊列, 但是主線程還在執(zhí)行, 所以只能等待
- syncFn 執(zhí)行完成, 此時 asyncFn 從事件隊列中進入主線程執(zhí)行
所以有時候會出現(xiàn)代碼實際執(zhí)行時間比延時時間長的情況。
宏任務(wù)和微任務(wù)
之前我們說過異步任務(wù)會進入到事件隊列中, 不同類型的任務(wù)會進入到不同的隊列中, 比如宏任務(wù)會進入到宏任務(wù)隊列中, 微任務(wù)會進入到微任務(wù)隊列中.
我們只要記住 當(dāng)當(dāng)前執(zhí)行棧執(zhí)行完畢時會立刻先處理所有微任務(wù)隊列中的事件,然后再去宏任務(wù)隊列中取出一個事件。同一次事件循環(huán)中,微任務(wù)永遠在宏任務(wù)之前執(zhí)行
這時候我們就可以解釋一開始的代碼執(zhí)行結(jié)果了:
- 主線程執(zhí)行按順序代碼
- 遇到 setTimeout, 回調(diào)進入到宏任務(wù)隊列上
- 遇到 Promise, 立即執(zhí)行, then 函數(shù)進入到微任務(wù)隊列
- 同步代碼執(zhí)行結(jié)束, 主線程檢查是否存在微任務(wù), 發(fā)現(xiàn) then, 執(zhí)行
- 微任務(wù)執(zhí)行完畢, 再去查找宏任務(wù) setTimeout, 執(zhí)行
- setTimeout 執(zhí)行結(jié)束, 檢查是否存在微任務(wù), 不存在, 結(jié)束.
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。
- JavaScript事件循環(huán)及宏任務(wù)微任務(wù)原理解析
- JS事件循環(huán)機制event loop宏任務(wù)微任務(wù)原理解析
- 詳解node.js 事件循環(huán)
- 深入分析JavaScript 事件循環(huán)(Event Loop)
- javascript事件循環(huán)event loop的簡單模型解釋與應(yīng)用分析
- Nodejs監(jiān)控事件循環(huán)異常示例詳解
- 前端js中的事件循環(huán)eventloop機制詳解
- 詳解JS瀏覽器事件循環(huán)機制
- 深入淺析Node.js 事件循環(huán)、定時器和process.nextTick()
- 實例分析js事件循環(huán)機制
- 實例分析JS與Node.js中的事件循環(huán)
- 在實例中重學(xué)JavaScript事件循環(huán)
相關(guān)文章
JavaScript中關(guān)于Object.create()的用法
這篇文章主要介紹了JavaScript中關(guān)于Object.create()的用法,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-02-02
JavaScript獲取當(dāng)前url根目錄(路徑)
本文主要介紹JavaScript獲取當(dāng)前url根目錄的方法,比較實用,需要的朋友可以參考一下。2016-06-06
uniapp uni-swipe-action 滑動操作狀態(tài)恢復(fù)功能實現(xiàn)
按照uni-app官方文檔的寫法,當(dāng)前一條滑動確認之后頁面列表刷新但是滑動的狀態(tài)還在,我們需要在滑動確認之后 頁面刷新 滑動狀態(tài)恢復(fù),下面小編給大家分享uniapp uni-swipe-action 滑動操作狀態(tài)恢復(fù)功能實現(xiàn),感興趣的朋友跟隨小編一起看看吧2024-06-06
解決window.history.back()返回上一頁有時候需要點擊多次問題
這篇文章主要介紹了解決window.history.back()返回上一頁有時候需要點擊多次問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-03-03
QQ跳轉(zhuǎn)支付寶并自動領(lǐng)紅包腳本(最新)
這篇文章主要給大家分享介紹了一個QQ跳轉(zhuǎn)支付寶并自動領(lǐng)紅包腳本,這個腳本應(yīng)該是最新的,測試后是可以使用的,文中給出了完整的示例代碼和使用方法,需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-06-06
javascript將16進制的字符串轉(zhuǎn)換為10進制整數(shù)hex
這篇文章主要介紹了javascript將16進制的字符串轉(zhuǎn)換為10進制整數(shù)hex,需要的朋友可以參考下2020-03-03

