技術總結——JS的執行順序
最近做了一些大廠的面試題目,才發現對JS的運行機制掌握的還是很淺薄,看了不少文檔、博客後在這裏做一個簡單的總結
基礎知識
我們都知道,爲了避免對DOM的操作產生衝突(JS誕生之初只是爲了給頁面賦予一些動態效果),JS只有一個線程。即便是現在有了webworker來實現多線程進行任務處理,但實際上,JS的多線程其實是單線程模擬出來的。
所以,爲了保證JS的執行效率,異步成爲了JS的一個核心的技術點。
以往的印象
按照我之前的理解,所謂JS的異步操作就是註冊一個回調函數,當觸發回調函數的事件滿足了,就掛起當前執行的任務,轉而執行回調函數。
事實證明我把JS想的太簡單了~
事實一,事件循環
- 首先,一門單線程語言從上向下執行
- 當遇到同步任務,讓同步任務進入主線程開始執行;當遇到異步任務,註冊異步任務的回調
- 當異步任務的觸發條件完成,回調函數進入事件隊列
- 當主線程中的同步任務完成,將事件隊列中的任務拉入主線程
- 進入步驟2,繼續~
上面就是JS的執行順序,用一個簡單的栗子說明一下
function foo(){
console.log('異步回調執行');
}
console.log('第一個同步任務');
setTimeout(foo);
console.log('第二個同步任務');
上面代碼的執行順序是:
- 顯示
第一個同步任務
- 然後執行
setTimeout
將foo註冊爲回調,因爲我們沒有寫延時,所以foo進入了事件隊列 - 接下來顯示
第二個同步任務
,至此主線程空了 - foo被拉入主線程開始執行,顯示
異步回調執行
事實二,宏任務和微任務
除了同步任務和異步任務的差別,有時我們還需要進一步對任務進行劃分,也就是宏任務和微任務。
- 宏任務主要包括script,setTimeout,setInterval
- 微任務主要包括Promise,process.nextTick
隨着任務的細化,任務隊列也進一步分開了分爲宏任務隊列和微任務隊列。
這時的執行順序如下:
- 首先script作爲第一個宏任務進入主線程開始執行
- 自上而下執行,當遇到同步任務直接在主線程執行;當遇到異步宏任務,進入宏任務隊列;當遇到異步微任務,進入微任務隊列
- 當前宏任務(第一次是script)結束後,檢查微任務隊列是否爲空,如果不爲空就拉入主線程執行。
- 當微任務隊列執行完成後,從宏任務隊列拉取一個宏任務進入主線程執行,轉到步驟3
我們還是舉一個栗子
setTimeout(function() {
new Promise(function(resolve) {
console.log('promise');
}).then(function() {
console.log('then又是一個微任務');
})
console.log('setTimeout是一個宏任務');
})
new Promise(function(resolve) {
console.log('promise');
}).then(function() {
console.log('then是一個微任務');
})
console.log('console');
這段代碼的執行順序如下:
- 首先整段代碼作爲一個宏任務,進入主線程執行
- 遇到第一個
setTimeout
,將他的回調函數放到宏任務隊列 - 遇到下面的
promise
將then
,放到微任務隊列 - 執行
console.log
,第一個宏任務結束,開始拉取微任務隊列,顯示then是一個微任務
,微任務隊列空 - 進入宏任務隊列執行
setTimeout
的回調函數,將新的promise.then
放到微任務隊列,顯示setTimeout是一個宏任務
,當前宏任務結束,拉取微任務隊列 - 顯示
then又是一個微任務
,微任務隊列空 - 宏任務和微任務全部執行完成,程序執行完成
我的理解大致就是這樣,如果有哪裏寫的不正確歡迎指正~