呼叫堆疊
無意之中發現一個很不錯的專案:中文地址 、原版英文地址 ,剛好適合自己鞏固加深理解JavaScript,於是定下一個小目標:總共33個知識點,每天攻克一個,一個月後再來回首感悟!
內容:
1.js是一個單執行緒的程式語言
意味著js在同一時間段內只能做一件事情,意味著它只有一個呼叫堆疊(call stack)。為什麼呢?因為js是瀏覽器指令碼語言,主要用來處理與使用者進行的操作、操作DOM之類的事情,如果它有多個執行緒,比如執行緒A想新增DOM節點,執行緒B想刪除該DOM節點,那它該何去何從呢?
2.Event Loop(事件迴圈)
上圖來自於優秀的這篇文章同步任務和非同步任務分別進入不同的“場所”。同步進入主執行緒,非同步進入Event Table並註冊回撥函式,然後將其移入進Event Queue中。主執行緒內的任務執行完畢直至為空後,再去Event Queue讀取對應的函式,進入主執行緒。
上述過程不斷重複,就是 Event Loop(事件迴圈)
3.巨集任務(macro tasks)和微任務(micro tasks)
-
js中有兩類任務佇列:巨集任務(macro tasks)和微任務(micro tasks).
- 巨集任務:script(全域性任務)、setTimeout、setInterval、setImmediate、I/O、UI rendering
- 微任務:process.nextTick、Promise, Object.observer, MutationObserver
4.一些面試題
setTimeout(_ => console.log(4)) new Promise(resolve => { resolve() console.log(1); }).then(_ => { console.log(3); }) console.log(2); 複製程式碼
- setTimeout屬於巨集任務,new Promise屬於同步任務,於是直接輸出 1
- 後面的.then()加入微任務中,接下來直接輸出 2
- 微任務.then()比setTimeout先執行,故輸出 3
- 最後輸出 4
-
敲黑板:
- setTimeout的作用是等待給定的時間後為它的回撥產生一個新的巨集任務
- Promise.then則是具有代表性的微任務
- new Promise在例項化的過程中所執行的程式碼都是 同步 進行的,而then中註冊的回撥才是 非同步 執行的
- 同步程式碼執行完成後才回去檢查是否有非同步任務完成,並執行對應的回撥,而 微任務又會在巨集任務之前執行
setTimeout(function(){ console.log('定時器開始啦') }); new Promise(function(resolve){ console.log('馬上執行for迴圈啦'); for(var i = 0; i < 10000; i++){ i == 99 && resolve(); } }).then(function(){ console.log('執行then函式啦') }); console.log('程式碼執行結束'); 複製程式碼
執行結果:馬上執行for迴圈啦,程式碼執行結束,執行then函式啦, 定時器開始啦。(解析步驟同上!)
console.log(1); setTimeout(() => { console.log(2); Promise.resolve().then(() => { console.log(3) }); }); new Promise((resolve, reject) => { console.log(4) resolve(5) }).then((data) => { console.log(data); }) setTimeout(() => { console.log(6); }) console.log(7); 複製程式碼
- 執行結果:1、4、7、5、2、3、6
-
敲黑板:
- 執行全域性Script,直接輸出 1 ,後面的setTimeout為巨集任務
- new Promise相當於同步任務,輸出 4 ,後面的.then()加入到微任務佇列中,後面的setTimeout為巨集任務
- 接著執行全域性Script,直接輸出 7
- 執行完所有的巨集任務後,接著在微任務佇列中的所有,輸出 5
- 接著執行剩下的巨集任務,輸出 2
- 然後執行上一步巨集任務後產生的微任務,輸出 3
- 最後執行最後一個setTimeout巨集任務,輸出 6
console.log('script start'); setTimeout(function() { console.log('setTimeout'); }, 0); Promise.resolve().then(function() { console.log('promise1'); }).then(function() { console.log('promise2'); }); console.log('script end'); 複製程式碼
- 執行結果:script start、script end、 promise1、 promise2、setTimeout
-
敲黑板:
- 全域性Script任務,直接輸出:script start、script end
- 接下來執行微任務,輸出promise1、promise2
- 最後輸出setTimeout
- 所有微任務總會在下一個巨集任務之前全部執行完畢