Event loop 與 Async Await 比較
Node.js Event loop 與 Async Await 比較
JS 原理
JS 為何是單線程的
最初設計JS時,主要是用來驗證瀏覽器DOM元素的一個語言,如果js是多線程,那麼兩個線程同時對一個DOM元素會進行相互衝突的操作 這樣瀏覽器是無法執行的
JS 為何需要非同步
如果JS沒有非同步,那麼程式由上而下執行,如果遇到上一行解析時間很長,那麼下面的代碼就會被阻塞。對用戶而言阻塞=卡死。就會有很糟的體驗
JS 單線程是如何實現非同步的呢
JS 的非同步以及多線程都可以理解為一個假象。因為其實就是透過事件循環(Event Loop),去調配單一線程的執行順序。 所以理解Event Loop 就可以理解JS非同步的運行機制。
Event Loop
為何要弄懂 Event Loop
- 掌握底層原理,可以讓自己以不變應萬變
- 增加深度,了解JS運行機制
Stack、Queue、Heap
堆 Heap
堆為一種數據結構,利用而二元樹所獲得的一種數據結構。一種為最小堆,另一種為最大堆
堆疊 Stack
Call Stack
- 一種資料結構
- 後進先出的概念
- 只能在最尾端插入後刪除的線性表
Callback Queue or Event Queue
- 一種資料結構
- 先進先出的概念
- 在結構的前端進行刪除,在結構的後端進行插入
- 沒有元素時稱為空陣列
任務種類
- 一種是宏任務(MacroTask)
- 一種是微任務(MicroTask) 不管宏任務or微任務都是進入 Event Queue
宏任務(MacroTask)
- script 全部代碼
- I/O
- UI Rendeting
- MessageChannel
- postMessage
微任務(MicroTask)
- Process.nextTick(Node.js)
- Promise
- Object.observe(廢棄)
- MutationObserver
queues 非同步事件
- setTimeout、setInterval、setImmediate
- click
- ajax
調用線怎麼走的
JS調用執行線為 後進先出(Stack)
的規則,當函數執行時,會被添加到線的頂部,當調用執行線執行完成後,就會從頂部移出,直到線內被清空
Event Loop 進程
- 「調用執行線」執行完同步任務後,會去看調用執行線是否為空
- 如果調用執行線為空,會去檢查微任務是否為空
- 為空:執行宏任務
- 不為空:執行微任務
- 每次單個宏任務執行完畢後,檢查微任務(Callback Queue)隊列是否為空,不為空則依「先入先出」規則做
- 設置微任務隊(Callback Queue)為 null,然後執行宏任務,如此循環
microTask 進程
- 設置 microTask 檢查點標誌為true
- 當事件循環 microtask 執行不為空時:選擇一個最先進入 microtask 陣列的 microtask。將事件循環 microtask 設置為以選擇的 microtask,運行此 microtask,將已經執行完的 microtask 為null,移除此 microtask
- 清除 indexDB 事務
- 設置進入 microtask 檢查點的標誌為 false
分析 Event Loop
- 不同類型的任務會進入到對應的Event Queue
- setTimeout or setInterval 會進入宏任務的Event Queue
- Promise or process.nextTick會進入微任務的Event Queue
- 遇到 promise.then 之類的微任務,會讓這個微任務被安排在當前的宏任務中
- 當執行完一輪的同步+非同步腳本後,就往下一輪邁進
async/await
async/await 是什麼
我們創建了promise但不能同步等他執行完成。我們只能通過 then 傳一個 callback function,這樣很容易再次陷入 callback 地獄。 所以實際上 async/await 在底層轉換了 Promise and then 的回調函數。也就是 Promise 的語法糖
- async 是非同步的縮寫
- await 是等待非同步方法執行完成
async/await 用來做啥 優化 promise的回調問題
async/await 內部做了什麼
- async 返回一個 Promise 物件,如果再函數中 return 一個直接量,async 會把這個量通過 Promise.resolve() 封裝成 Promise 物件。 如果你返回 Promise 就以你返回的Promise為主
- await 是等待,等待運行的返回值。await 後面通常是一個 Promise。但這不代表 await 後面只能跟非同步操作
await 等待機制
如果後面接了 Promise,就會阻塞後面的代碼,等著 Promise 對象 resolve,然後得到 resolve 值作為 await 表達式的運算結果
async/await 在什麼場景下使用
如果由 多個 Promise 組成的 then 鏈
時,就可以使用
async/await 分析方向
- async 定義的是一個 Promise 函數,和普通函數一樣只要不調用就不會進入事件陣列
- async 內部如果沒有主動 return promise,那 async 會把函數的返回值用 Promise 包裝
- await 必須出現在 async 函數內,await 不一定要跟一個非同步操作,也可以是一個普通表達式
- 遇到 await 關鍵字,await 右邊的語句會立即被執行,然後 await 下面的代碼進入等待狀態,等待結果
- await 後面如果不是 promise對象,await 會為阻塞後面的代碼,先執行 async外面的同步代碼,同步執行完後,再回來 async 內部 ,把這個非 promise 的東西,作為 await 表達式的結果
- await 後面如果是 promise,await 也會暫停 async 後面的 code,先執行 async 外面的代碼,等著 Promise 對象 fulfilled, 然後把 resolve 的參數作為 await 表達式的運算結果
分析原則
- 遇到 new Promise 是直接執行其任務
- new Promise 接的 then 是微任務
- await 會等到 async 內的事情都做完才往下做同個 function 內的事情
- await function 會去驅動這個 function,function 做完,再往同層的同步走下去
- async 回傳 Promise & async 中的 return 會被當作 resolve value
- 下面這串算同步指令
await new Promise((res){
console.log('1')
})