トップレベルの await を使用すると、モジュールを非同期関数として動作させることができます。つまり、モジュールの実行を一時停止して Promise を待機できます。これは、ポリフィルや重いセカンダリ ライブラリなどの非同期依存関係を、ブラウザで必要な場合にのみ条件付きで読み込む場合に非常に便利です。
トップレベルの await を利用することで、条件付き読み込みロジックを単一のモジュール内にカプセル化し、依存関係が完全に読み込まれて準備が整うまで、下流のコンシューマ モジュールが実行されないように効果的に防止できます。
条件付きポリフィル パターン
トップレベルの await を使用すると、モジュールである非同期依存関係を条件付きで読み込むことができます。条件付き依存関係読み込みパターンの適切な使用例は、特定の機能をサポートしていないブラウザ用のポリフィルを条件付きで読み込むことです。このアプローチでは、機能検出と動的インポートを単一の依存関係モジュール内にカプセル化します。
次の例では、popover 属性のポリフィルが HTMLElement.prototype に存在しない場合に、条件付きで読み込まれます。
// 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は、ポリフィルの読み込みが完了する前に、すべてのブラウザで同時に評価されます。 - 複数の兄弟モジュールから最上位の
awaitモジュールを同時にインポートしないでください(たとえば、a.jsとb.jsの両方で./conditionally-load-polyfill.jsを静的にインポートするなど)。Safari 27 では WebKit のモジュール ローダーが修正され、仕様に従って最上位のawaitモジュールの同時インポートがサポートされるようになりましたが、Safari 15 ~ 26.6 では、複数のモジュールが最上位のawaitを含むモジュールを同時にインポートすると失敗します(MDN ブラウザの互換性を参照)。
代わりに、モジュール構造に応じて次の 2 つのパターンのいずれかを選択します。
- 単一のエントリ モジュール(上記の
main.jsを参照): エントリ モジュールが依存初期化ロジックを直接実行する場合は、そのモジュールの上部で./conditionally-load-polyfill.jsを静的にインポートし、その本文でコードを実行します。 - 個別のアプリケーション モジュールのブートストラップ: エントリ モジュールがダウンストリーム アプリケーション モジュール(
./app.jsなど)に委任する場合は、動的なimport()呼び出しを最上位のawaitとチェーンして、ポリフィル モジュールが解決された後にのみ./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');
代替戦略
基本的なトップレベルの await は、2021 年以降、主要なブラウザ(Chrome 89、Edge 89、Firefox 89、Safari 15)でサポートされていますが、仕様に完全に準拠したのは Safari 27 からです(MDN ブラウザの互換性をご覧ください)。
- Safari 15 ~ 26.6: 前のセクションの単一エントリ ポイント パターンに従い、複数の兄弟モジュールがトップレベルの
awaitモジュールを同時にインポートしない限り、追加のフォールバックなしでトップレベルのawaitを安全に使用できます。 - レガシー ブラウザ(2021 年以前): アプリケーションが、トップレベルの
awaitをサポートしていないブラウザをサポートする必要がある場合は、トップレベルのawaitを避け、条件付きimport()オーケストレーションを標準のasync関数または.then()Promise チェーンでラップします。