頂層 await
讓開發人員可以在非非同步函式之外使用 await
關鍵字。它就像一個大型非同步函式,導致 import
它們的其他模組在開始評估其主體之前等待。
舊行為 #
當 async
/await
首次推出時,嘗試在 async
函式之外使用 await
會導致 SyntaxError
。許多開發人員利用立即呼叫的非同步函式表達式來取得該功能。
await Promise.resolve(console.log('🎉'));
// → SyntaxError: await is only valid in async function
(async function() {
await Promise.resolve(console.log('🎉'));
// → 🎉
}());
新行為 #
使用頂層 await
時,上述程式碼會在 模組 中以您預期的方式運作
await Promise.resolve(console.log('🎉'));
// → 🎉
注意:頂層 await
僅 在模組的頂層運作。不支援傳統指令碼或非非同步函式。
使用案例 #
這些使用案例取自 規格提案存放庫。
動態相依路徑 #
const strings = await import(`/i18n/${navigator.language}`);
這允許模組使用執行時期值來確定相依性。這對於開發/生產分割、國際化、環境分割等事項很有用。
資源初始化 #
const connection = await dbConnector();
這允許模組表示資源,並在無法使用模組時產生錯誤。
相依備援 #
以下範例嘗試從 CDN A 載入 JavaScript 函式庫,如果失敗則備援到 CDN B
let jQuery;
try {
jQuery = await import('https://cdn-a.example.com/jQuery');
} catch {
jQuery = await import('https://cdn-b.example.com/jQuery');
}
模組執行順序 #
頂層 await
對 JavaScript 最大的變更之一是圖表中模組的執行順序。JavaScript 引擎以 後序遍歷 執行模組:從模組圖表的左子樹開始,評估模組,匯出其繫結,然後執行其同層節點,接著是其父節點。此演算法會遞迴執行,直到執行模組圖表的根節點。
在頂層 await
之前,此順序總是同步且確定性的:在多次執行您的程式碼之間,您的圖表保證會以相同的順序執行。一旦頂層 await
上線,就會有相同的保證,但僅在您不使用頂層 await
的情況下。
當您在模組中使用頂層 await
時,會發生下列情況
- 目前模組的執行會延遲,直到等待中的承諾已解決。
- 父模組的執行會延遲,直到呼叫
await
的子模組及其所有同層模組匯出繫結。 - 同層模組和父模組的同層模組能夠以相同的同步順序繼續執行,前提是圖形中沒有循環或其他
await
的承諾。 - 呼叫
await
的模組會在await
的承諾解決後繼續執行。 - 父模組和後續樹狀結構會繼續以同步順序執行,只要沒有其他
await
的承諾。
這不是已經在 DevTools 中運作了嗎?#
確實如此!Chrome DevTools、Node.js 和 Safari Web Inspector 中的 REPL 已經支援頂層 await
一段時間了。然而,此功能是非標準的,且僅限於 REPL!它不同於頂層 await
提案,後者是語言規格的一部分,且僅適用於模組。若要以完全符合規格提案語意的方式測試依賴於頂層 await
的生產程式碼,請務必在您的實際應用程式中進行測試,而不要只在 DevTools 或 Node.js REPL 中進行測試!
頂層 await
不會造成問題嗎?#
您可能看過 臭名昭著的 gist,由 Rich Harris 所撰寫,最初概述了關於頂層 await
的一些疑慮,並敦促 JavaScript 語言不要實作此功能。一些具體疑慮包括
- 頂層
await
可能會阻擋執行。 - 頂層
await
可能會阻擋擷取資源。 - 對於 CommonJS 模組,不會有明確的互通性。
提案的第 3 階段版本直接解決了這些問題
- 由於同層模組能夠執行,因此沒有明確的阻擋。
- 模組圖表的執行階段中會發生頂層
await
。此時,所有資源都已擷取並連結。不會有封鎖擷取資源的風險。 - 頂層
await
僅限於模組。明確不支援腳本或 CommonJS 模組。
與任何新的語言功能一樣,總是有發生意外行為的風險。例如,使用頂層 await
時,循環模組相依性可能會造成死結。
在沒有頂層 await
的情況下,JavaScript 開發人員經常使用非同步立即呼叫函式表達式,只是為了存取 await
。很遺憾地,這種模式會導致圖表執行較不確定,以及應用程式的靜態分析能力較差。基於這些原因,缺少頂層 await
被視為比功能中引入的危害更高的風險。
支援頂層 await
#
- Chrome: 自 89 版起支援
- Firefox: 不支援
- Safari: 自 15 版起支援
- Node.js: 自 14 版起支援
- Babel: 不支援