概述
最近看了一些非同步的文章,有一些作者沒有寫程式碼也把錯誤的理解放上來。想想,我也應該總結一些,之前面試也有過一道題目,雖然說是考察非同步,但其實就是考察非同步當中的任務佇列。給你一道題目,你覺得會依次輸出什麼?
console.log('1')
Promise.resolve().then(() => console.log('2'))
setTimeout(() => {console.log('3'); Promise.resolve().then(() => console.log('4'))}, 0)
Promise.resolve().then(() => console.log('5'))
setTimeout(() => console.log('6'), 0)
console.log('7')
答案是
1 7 2 5 3 4 6
如果你知道為什麼會輸出這些的話,那我想你不必看下面了,因為你也有大概的理解,如果沒有的話,我就跟你分析一下吧。
先看一張圖吧,是拿別人的,有部分原因也是因為他寫的文章有錯誤,我才總結。
先理解這張圖片吧,我簡單介紹一下。
- 棧:主執行緒的函式執行,非同步操作的執行放在了非同步處理模組。
- 堆:用來儲存引用型別的指向。
- 非同步處理模組:主執行緒裡面的非同步模組。
- 任務佇列:儲存非同步執行緒的執行佇列。
然後,js執行就是執行主執行緒->執行任務佇列
當然,這只是大概的介紹,真正的堆和棧並不是和他說的一樣,棧裡面還有記憶體棧和呼叫棧,記憶體棧又有全域性的記憶體棧,也有某個函式的記憶體棧,當然,函式內部的記憶體棧又放在了堆裡面。這裡面的棧,僅僅是代表了呼叫棧。
巨集任務佇列(macrotasks)
什麼是巨集任務佇列?
巨集任務佇列macrotasks: setTimeout, setInterval, setImmediate, I/O, UI rendering
上面的基本操作就是巨集任務佇列
微任務佇列(microtasks)
微任務佇列microtasks: process.nextTick, Promise, MutationObserver
上面的基本操作就是微任務佇列
Event Loop
我就簡單的說一下js裡面執行順序吧:
- 執行主執行緒,如果有非同步操作,則放到非同步佇列執行。執行2
- 當主執行緒執行完畢,判斷非同步佇列是否有微任務,如果有,則新增進去主執行緒執行;如果沒有則將最新可以執行的巨集任務加進主執行緒。返回1
是不是很簡單?
那麼上面那道題的結果無非就是
// 一開始,主執行緒
console.log('1');
console.log('7');
// 下一步,主執行緒
Promise.resolve().then(() => console.log('2'))
// 下一步,主執行緒
Promise.resolve().then(() => console.log('5'))
// 下一步,主執行緒
setTimeout(() => {console.log('3')}, 0)
// 下一步,主執行緒
Promise.resolve().then(() => console.log('4'))
// 下一步,主執行緒
setTimeout(() => console.log('6'), 0)
總結
看了一下很多文章,以為很難,很想畫圖,但是在寫的過程中,發現其實真的很簡單,只要好好了解js裡面的引擎就好了,js還是一個很強大的單執行緒語言。