Bedingte asynchrone Abhängigkeiten

Webfunktionen: Top-Level-Await

Mit await auf oberster Ebene können Module als asynchrone Funktionen fungieren. Das bedeutet, dass sie die Modulausführung pausieren können, um auf Promises zu warten. Das ist besonders nützlich, um asynchrone Abhängigkeiten wie Polyfills oder umfangreiche sekundäre Bibliotheken nur dann bedingt zu laden, wenn sie vom Browser benötigt werden.

Durch die Verwendung von „await“ auf oberster Ebene können Sie die bedingte Logik zum Laden in einem einzigen Modul kapseln. So wird verhindert, dass nachgelagerte Module ausgeführt werden, bevor die Abhängigkeit vollständig geladen und bereit ist.

Bedingtes Polyfill-Muster

Mit await auf oberster Ebene können Sie jede asynchrone Abhängigkeit, die ein Modul ist, bedingt laden. Ein gutes Beispiel für das bedingte Laden von Abhängigkeiten ist das bedingte Laden von Polyfills für Browser, die ein bestimmtes Feature nicht unterstützen. Bei diesem Ansatz werden die Featureerkennung und der dynamische Import in einem einzigen Abhängigkeitsmodul zusammengefasst.

Im folgenden Fall wird das Polyfill für das Attribut popover bedingt geladen, wenn es nicht in HTMLElement.prototype vorhanden ist:

// 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();
}

Sequenzierung von Modulimporten und Vermeidung des Fehlers vor Safari 27

OBLIGATORISCH: Sie müssen Ihren Modulgraphen so strukturieren, dass das await-Modul auf oberster Ebene von einem einzelnen Einstiegspunkt importiert wird und abgeschlossen ist, bevor nachgelagerte Module ausgewertet werden:

  • Platzieren Sie KEINE statischen Importe auf derselben Ebene in einer Eintragsdatei (z. B. import './conditionally-load-polyfill.js'; import './app.js';). Gemäß der ECMAScript-Modulbewertungsspezifikation wird durch await auf oberster Ebene nur das übergeordnete Modul, das es importiert, ausgesetzt, nicht aber statische Importe auf derselben Ebene. Daher wird ./app.js in allen Browsern gleichzeitig ausgewertet, bevor das Polyfill vollständig geladen ist.
  • Importieren Sie das await-Modul auf oberster Ebene NICHT gleichzeitig aus mehreren gleichgeordneten Modulen (z. B. wenn sowohl a.js als auch b.js ./conditionally-load-polyfill.js statisch importieren). In Safari 27 wurde der Modullader von WebKit korrigiert, um gleichzeitige Importe von await-Modulen auf oberster Ebene gemäß Spezifikation zu unterstützen. In Safari 15–26.6 schlägt der Import jedoch fehl, wenn mehrere Module gleichzeitig ein Modul importieren, das ein await auf oberster Ebene enthält (siehe MDN-Browserkompatibilität).

Wählen Sie stattdessen je nach Modulstruktur eines der beiden folgenden Muster aus:

  1. Einzelnes Einstiegsmodul (oben in main.js dargestellt): Wenn Ihr Einstiegsmodul die abhängige Initialisierungslogik direkt ausführt, importieren Sie ./conditionally-load-polyfill.js statisch am Anfang dieses Moduls und führen Sie Ihren Code im Text aus.
  2. Separate Anwendungsmodule bootstrappen: Wenn Ihr Einstiegsmodul an nachgelagerte Anwendungsmodule (z. B. ./app.js) delegiert, verketten Sie dynamische import()-Aufrufe mit await auf oberster Ebene, sodass ./app.js erst abgerufen und ausgewertet wird, nachdem das Polyfill-Modul aufgelöst wurde:
// 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');

Fallback-Strategien

Die grundlegende await-Funktion auf oberster Ebene wird seit 2021 von den wichtigsten Browsern (Chrome 89, Edge 89, Firefox 89 und Safari 15) unterstützt. Die vollständige Spezifikation wurde jedoch erst in Safari 27 erreicht (siehe MDN-Browserkompatibilität).

  • Safari 15–26.6: Wenn Sie das Muster mit einem einzigen Einstiegspunkt im vorherigen Abschnitt befolgen, sodass mehrere gleichgeordnete Module nicht gleichzeitig das await-Modul auf oberster Ebene importieren, können Sie sich ohne zusätzlichen Fallback auf await auf oberster Ebene verlassen.
  • Ältere Browser (vor 2021): Wenn Ihre Anwendung Browser ohne Unterstützung für await auf oberster Ebene unterstützen muss, vermeiden Sie await auf oberster Ebene und verpacken Sie Ihre bedingte import()-Orchestrierung in eine Standard-async-Funktion oder eine .then()-Promise-Kette.