JS非同步堆疊追蹤之為什麼await勝過Promise
概述
async/await和Promise的根本區別在於await fn()暫停當前函式的執行,而promise.then(fn)在將fn呼叫新增到回撥鏈後,繼續執行當前函式。
const fn = () => console.log('hehttp://www.cppcns.comllo')
const a = async () => {
await fn() // 暫停 fn 的執行
}
// 呼叫 a 時,才恢復 fn 的執行
a() // "hello"
const promise = Promise.resolve()
// 將 fn 新增到回撥鏈後,繼續執行 fn
promise.then(fn) // "hello"
在堆疊追蹤的上下文中,這種差異非常顯著。
當一個Promise鏈(無論是否脫糖化)在任何時候丟擲一個未經處理的異常時,javascript引擎都會顯示一條錯誤資訊和(希望)記錄一個有用的堆疊追蹤。
作為一名開發人員,無論您使用的是普通的Promise還是async await,您都會期望這樣。
Promise
想象一個場景,當對非同步函式b的呼叫解析時,呼叫函式c:
const b = () => Promise.resolve() const a = () => { b().then(() => c()) }
當呼叫a時,將同步發生以下情況:
- b被呼叫並返回一個Prom
- .then回撥(實際上是呼叫c())被新增到回撥鏈中( V8 術語中,[…]被新增為解析處理程式)。
之後,我們完成了在函式a的主體中執行程式碼。a永遠不會被掛起,當對b的非同步呼叫解析時,上下文已經消失了。
想象一下如果b(或c)非同步丟擲異常會發生什麼?理想情況下,堆疊追蹤應該包括a,因為b(或c)是從那裡呼叫的,對吧?既然我們不在參考a了 ,那怎樣能做到呢?
為了讓它工作,javaScript 引擎需要程式設計客棧在上面的步驟之外做一些事情:它在有機會的時候捕獲並存儲堆疊追蹤。
在V8中,堆疊追蹤附加到b返回的Promise。當Promise實現時,堆疊追蹤將被傳遞,以便c可以根據需要使用它。
b()[OcouEZiYia] -> b().then()[a] -> c[a?:a]
捕獲堆疊追蹤需要時間(即降低效能);儲存這些堆疊追蹤需要記憶體。
async/await
下面是同樣的程式,使用async/await而程式設計客棧不是Promise編寫:
const b = () => Promise.resolve() const a = async () => { await b() c() }
使用await,即使在await呼叫中不收集堆疊追蹤,我們也可以恢復呼叫鏈。
這是可能的,因為a被掛起,正在等待b解決。如果b丟擲異常,則可以按需以這種方式重建堆疊追蹤。
如果c丟擲異常,堆疊追蹤可以像同步函式那樣構造,因為發生這種情況時,我們仍在a上下文中。
通過遵循以下建議,使 JavaScript 引擎能夠以更高效的方式處理堆疊追蹤:
- 偏好async/await勝過Promise。
- 使用 @babel/preset env避免不必要的async/await傳輸。
以上就是js非同步堆疊追蹤之為什麼await勝過Promise的詳細內容,更多關於Javascript的資料請關注我們其它相關文章!