條件式非同步依附元件

網頁功能: 頂層 await

頂層 await 可讓模組做為非同步函式,也就是說,模組可以暫停執行,等待 Promise。這項功能非常實用,可讓您在瀏覽器需要時,才載入非同步依附元件 (例如 polyfill 或大型次要程式庫)。

只要使用頂層 await,就能將條件式載入邏輯封裝在單一模組中,有效防止下游消費者模組在依附元件完全載入並準備就緒前執行。

條件式 Polyfill 模式

雖然頂層 await 可用於有條件地載入任何模組的非同步依附元件,但條件式依附元件載入模式的良好用途,是有條件地載入不支援特定功能的瀏覽器 polyfill。這個方法會將功能偵測和動態匯入封裝在單一依附元件模組中。

在以下情況中,如果 HTMLElement.prototype 沒有 popover 屬性,系統會視情況載入該屬性的 polyfill:

// conditionally-load-polyfill.js

// Check if the feature is missing before doing work.
// MANDATORY: Prefer checking HTMLElement.prototype over window or document
// when checking for a global DOM attribute or property like popover.
if (!('popover' in HTMLElement.prototype)) {
  // Use top-level await to pause the execution of any module that imports this file 
  // until the polyfill finishes downloading and executing.
  await import('/path/to/popover-polyfill.js');
}

// Export a marker if needed by your application
export const polyfillLoaded = true;
// main.js

// MANDATORY: Because conditionally-load-polyfill.js uses top-level await, 
// this import will block execution of main.js until the polyfill is ready.
import './conditionally-load-polyfill.js';

// Now it is safe to use the feature (e.g., showing a popover)
const myPopover = document.getElementById('my-popover');
if (myPopover) {
  myPopover.showPopover();
}

排序模組匯入作業,並避免 Safari 27 之前的錯誤

必要:您必須建構模組圖,讓頂層 await 模組從單一進入點匯入,並在下游模組評估前完成:

  • 請勿在項目檔案中放置同層靜態匯入 (例如 import './conditionally-load-polyfill.js'; import './app.js';)。根據 ECMAScript 模組評估規格,頂層 await 只會暫停匯入該模組的父項模組,而不會暫停同層靜態匯入,因此 ./app.js 會在所有瀏覽器中同時評估,直到 polyfill 載入完成。
  • 請勿同時從多個同層模組匯入頂層 await 模組 (例如讓 a.js 和 b.js 都靜態匯入 ./conditionally-load-polyfill.js)。雖然 Safari 27 已修正 WebKit 的模組載入器,可依規格同時匯入頂層 await 模組,但 Safari 15 至 26.6 在多個模組同時匯入含有頂層 await 的模組時會失敗 (請參閱 MDN 瀏覽器相容性)。

請改為根據模組結構,選擇下列其中一種模式:

  1. 單一進入模組 (如上方的 main.js 所示):如果進入模組直接執行依附元件初始化邏輯,請在該模組頂端靜態匯入 ./conditionally-load-polyfill.js,並在主體中執行程式碼。
  2. 啟動獨立應用程式模組:如果您的進入模組委派給下游應用程式模組 (例如 ./app.js),請使用頂層 await 鏈結動態 import() 呼叫,這樣系統只會在 Polyfill 模組解析後擷取及評估 ./app.js:
// main.js

// MANDATORY: Chain dynamic imports with top-level await when bootstrapping separate
// application modules so './app.js' waits for the polyfill to finish loading.
// DO NOT use sibling static imports (`import './conditionally-load-polyfill.js'; import './app.js';`),
// which evaluate concurrently and execute './app.js' before the polyfill is ready.
await import('./conditionally-load-polyfill.js');
await import('./app.js');

備用策略

雖然自 2021 年起,各大瀏覽器 (Chrome 89、Edge 89、Firefox 89 和 Safari 15) 都支援基本的頂層 await,但直到 Safari 27 才完全符合規格 (請參閱 MDN 瀏覽器相容性)。

  • Safari 15–26.6:只要遵循上一節的單一進入點模式,讓多個同層模組不會同時匯入頂層 await 模組,就能安全地依賴頂層 await,不需額外的備用方案。
  • 舊版瀏覽器 (2021 年前):如果應用程式必須支援不支援頂層 await 的瀏覽器,請避免使用頂層 await,並將條件式 import() 協調包裝在標準 async 函式或 .then() 承諾鏈中。