網(wǎng)站首頁 前端文檔 正文
一、四個概念
1、Javascript是單線程
單線程意味著我們的js
代碼只能從上往下同步執(zhí)行,同一時間只能執(zhí)行一個任務(wù),這會導(dǎo)致某些執(zhí)行時間較長或者執(zhí)行時間不確定的任務(wù)會卡住其它任務(wù)的正常執(zhí)行,Event Loop
出現(xiàn)的原因正是為了解決此問題。
2、任務(wù)隊列
為了解決上述的排隊問題,有了任務(wù)隊列,瀏覽器在異步任務(wù)有了結(jié)果后,將其添加到任務(wù)隊列,以待將來執(zhí)行,其他任務(wù)就在主線程上同步執(zhí)行。
這里要注意的是,向任務(wù)隊列中添加任務(wù)的時機是異步任務(wù)有結(jié)果后。其實任務(wù)隊列中存在的就是異步任務(wù)的回調(diào)函數(shù)。
3、同步任務(wù)、異步任務(wù)
Js程序中的同步任務(wù)是指在主線程中執(zhí)行的任務(wù),異步任務(wù)是指進(jìn)入任務(wù)隊列中的任務(wù)
4、Javascript執(zhí)行棧
所有的同步任務(wù)都在主線程上執(zhí)行,行成一個執(zhí)行棧。當(dāng)主線程上任務(wù)執(zhí)行完畢后,從任務(wù)隊列中取出任務(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ù)隊列。
- 執(zhí)行棧中的同步任務(wù)執(zhí)行完畢,此時任務(wù)隊列為空(未到1s),執(zhí)行棧也為空
- 異步任務(wù)有結(jié)果后,首先進(jìn)入任務(wù)隊列排隊(因為可能有很多異步任務(wù))。
- 執(zhí)行棧從任務(wù)隊列中取出任務(wù)開始同步執(zhí)行。
- 重復(fù)執(zhí)行第5步。
二、Event Loop
Js執(zhí)行棧不斷的從任務(wù)隊列中讀取任務(wù)并執(zhí)行的過程就是Event Loop
我們知道任務(wù)隊列中存放的是異步任務(wù)的結(jié)果,那么異步任務(wù)都有哪些了?
- 1、事件
Javascript
中的事件有很多,都是屬于異步任務(wù)。由瀏覽器接管,當(dāng)事件觸發(fā)時,將事件的回調(diào)加入的任務(wù)隊列中,在Js執(zhí)行棧中沒有任務(wù)時執(zhí)行。
- 2、Http請求
- 3、定時器
- 4、requestAnimationFrame等
宏任務(wù)(macrotask
)和微任務(wù)(microtask
)
在了解了任務(wù)隊列和Event Loop
后,我們知道了Js執(zhí)行棧從任務(wù)隊列中讀取任務(wù)執(zhí)行,但這個具體工程我們?nèi)蝿?wù)不清楚,這里引出了宏任務(wù)和微任務(wù)的的概念,幫助我們理解Event Loop。
進(jìn)入任務(wù)隊列中的異步任務(wù)回調(diào)分為了宏任務(wù)和微任務(wù), Js執(zhí)行棧執(zhí)行宏任務(wù)和微任務(wù)的規(guī)則如下圖所示。
Js執(zhí)行棧首先執(zhí)行一個宏任務(wù)(全局代碼) -> 從任務(wù)隊列中讀取所有微任務(wù)執(zhí)行 -> UI rendering(瀏覽器渲染界面) -> 從任務(wù)隊列讀取一個宏任務(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è)置的時間。 這是因為在setTimeout
指定時間到達(dá)后,雖然回調(diào)函數(shù)被添加到了任務(wù)隊列,但是此時Js執(zhí)行棧中可能有正在執(zhí)行的任務(wù),此回調(diào)需要等待Js執(zhí)行棧的任務(wù)執(zhí)行完畢后才有機會執(zhí)行,這就是setTimeout
延時問題。
三、實戰(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í)行機制來分析這道題:
1: 執(zhí)行全局任務(wù)中的同步代碼輸出:
1
4
8
這里需要注意的是Promise
接受的handle
函數(shù)是同步任務(wù),而then
方法是異步任務(wù),所以會直接輸出4。
2: 這時的任務(wù)隊列中有三個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ù) 隊列添加了一個微任務(wù)。此時任務(wù)隊列有了新的微任務(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ù)隊列。
- 任務(wù)隊列中的任務(wù)分為了宏任務(wù)和微任務(wù)。Js執(zhí)行??偸窍葓?zhí)行一個宏任務(wù),再執(zhí)行完所有微任務(wù)…
到此這篇關(guān)于Javascript
運行機制之Event Loop的文章就介紹到這了,更多相關(guān)Javascript運行機制Event Loop內(nèi)容請搜索AB教程網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持AB教程網(wǎng)!
原文鏈接:https://juejin.cn/post/7025775013722603550
相關(guān)推薦
- 2022-12-06 React中的路由嵌套和手動實現(xiàn)路由跳轉(zhuǎn)的方式詳解_React
- 2022-04-19 一起來了解c語言的str函數(shù)_C 語言
- 2023-05-06 Flutter學(xué)習(xí)之SliverList和SliverGird的使用詳解_Android
- 2022-08-01 MongoDB基礎(chǔ)之集合操作_MongoDB
- 2022-10-29 python實現(xiàn)k均值聚類(kMeans)基于numpy
- 2022-08-14 Nginx安裝配置詳解_nginx
- 2023-01-04 Opencv實現(xiàn)計算兩條直線或線段角度方法詳解_python
- 2022-04-15 python實現(xiàn)請求數(shù)據(jù)包簽名_python
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支