事件循環是js這門語言的一大特色。
瞭解事件循環機制,有助於平常開發中遇到的一些異步問題。
並且仍是前端面試一常常考點。
故本人結合一些文章和我的的一些開發經驗,淺淡一下
一,js是一門單線程語言
js的單線程
a. js是一門單線程的語言。這意味着它在同一時間,只能作同一件事。
b. 但爲了協調事件,用戶交互,UI渲染和網絡行爲交互等。
c. 防止主線程被阻塞,Event Loop便應運而生。
如: 發送一個網絡請求,須要等待必定時間,這個時間內主線程空閒出來作些其餘事;
複製代碼
爲何js是單線程?
a. js主要是運行在瀏覽器的腳步語言,主要是操做dom;
b. 舉個例子,若是js同時有多個線程。多個線程同時操做同一個dom,
這時瀏覽器該依據那個線程,如何判斷優先級
c. 爲了不上述問題,並下降複雜度,故js被設計成單線程語言。
複製代碼
二,概念的理解
同步任務
同步任務指的是,在主線程上排隊執行的任務,
只有前一個任務執行完畢,才能執行後一個任務;
複製代碼
異步任務
異步任務指的是,不進入主線程、而進入"任務隊列" (task queue)的任務,
只有"任務隊列" 通知主線程,某個異步任務能夠執行了,該任務纔會進入主線程執行。
複製代碼
異步執行機制
a. 全部同步任務都在主線程上執行,造成一個執行棧(execution context stack);
b. 主線程以外,還存在一個"任務隊列" (task queue)。
只要異步任務有了運行結果,就在"任務隊列" 之中放置一個事件。
c. 一旦"執行棧" 中的全部同步任務執行完畢,系統就會讀取"任務隊列" ,
看看裏面有哪些事件。那些對應的異步任務,因而結束等待狀態,
進入執行棧,開始執行。
d. 主線程不斷重複上面的第三步。
複製代碼
任務隊列
"任務隊列" 是一個先進先出的數據結構,排在前面的事件,優先被主線程讀取。
複製代碼
事件循環
主線程從"任務隊列" 中讀取事件,這個過程是循環不斷的,
因此整個的這種運行機制又稱爲Event Loop(事件循環)。
複製代碼
宏任務與微任務
異步任務分爲 宏任務(macrotask) 與 微任務 (microtask),
不一樣的API註冊的任務會依次進入自身對應的隊列中,
而後等待 Event Loop 將它們依次壓入執行棧中執行。
宏任務:script(總體代碼)、setTimeout、setInterval、UI 渲染、
I/O、postMessage、 MessageChannel、setImmediate(Node.js 環境)
微任務:Promise 、 MutaionObserver、process.nextTick(Node.js環境)
複製代碼
Event Loop(事件循環)
(1 )執行棧選擇最早進入隊列的宏任務(一般是script總體代碼),若是有則執行;
(2 )檢查是否存在 Microtask,若是存在則不停的執行,直至清空 microtask 隊列;
(3 )更新render(每一次事件循環,瀏覽器均可能會去更新渲染);
(4 )重複以上步驟;
複製代碼
宏任務 > 全部微任務(核心),上代碼
<script>// 宏任務1
console.log('宏任務1'); // 宏任務1中的同步任務
setTimeout(() => {// 宏任務1中的另外一個宏任務3
console.log('宏任務1中的另外一個宏任務3');
new Promise((resolve, reject) => {
resolve('宏任務3中的微任務2');
}).then(data => {// 宏任務3中的微任務2
console.log(data)
})
}, 300);
new Promise((resolve, reject) => {
resolve('宏任務1中的微任務1');
}).then(data => {// 宏任務1中的微任務1
console.log(data);
setTimeout(() => {// 微任務1中的另外一個宏任務4
console.log('微任務1中的另外一個宏任務4');
}, 300);
});
</script>// 宏任務1
<script>// 宏任務2
console.log('宏任務2')
</script>// 宏任務2
複製代碼
1. 宏任務1 =>宏任務1 中的微任務1
代表執行完宏任務就執行微任務(忽略宏任務2 ,便於理解)
2. 而後到 宏任務1 中宏任務3 =>宏任務3 中的微任務2
再次代表執行完本宏任務後就執行本宏任務下的微任務
3. 最後到微任務1 中的宏任務4
複製代碼