Warunkowe zależności asynchroniczne

Funkcje internetowe: Top-level await

Funkcja await najwyższego poziomu umożliwia modułom działanie jako funkcje asynchroniczne, co oznacza, że mogą one wstrzymywać wykonywanie modułu w oczekiwaniu na obietnice. Jest to niezwykle przydatne do warunkowego wczytywania zależności asynchronicznych, takich jak polyfille czy ciężkie biblioteki pomocnicze, tylko wtedy, gdy są one wymagane przez przeglądarkę.

Dzięki zastosowaniu oczekiwania na najwyższym poziomie możesz zamknąć logikę warunkowego wczytywania w jednym module, co skutecznie zapobiega wykonywaniu modułów konsumenckich na dalszych etapach, dopóki zależność nie zostanie w pełni wczytana i przygotowana.

Wzorzec warunkowego polyfilla

Chociaż instrukcji await najwyższego poziomu można używać do warunkowego wczytywania dowolnej zależności asynchronicznej, która jest modułem, dobrym zastosowaniem wzorca warunkowego wczytywania zależności jest warunkowe wczytywanie polyfilli w przypadku przeglądarek, które nie obsługują określonej funkcji. To podejście obejmuje wykrywanie funkcji i dynamiczny import w ramach jednego modułu zależności.

W tym przypadku polyfill atrybutu popover jest ładowany warunkowo, jeśli nie ma go w 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();
}

Sekwencjonowanie importu modułów i unikanie błędu w Safari 27

OBOWIĄZKOWE: musisz ustrukturyzować wykres modułu tak, aby moduł await najwyższego poziomu był importowany z jednego punktu wejścia i był ukończony przed oceną modułów podrzędnych:

  • NIE umieszczaj równorzędnych importów statycznych w pliku wejściowym (np. import './conditionally-load-polyfill.js'; import './app.js';). Zgodnie ze specyfikacją oceny modułu ECMAScript najwyższy poziom await zawiesza tylko moduł nadrzędny, który go importuje, a nie równorzędne importy statyczne, więc ./app.js będzie oceniany jednocześnie we wszystkich przeglądarkach, zanim polyfill zakończy wczytywanie.
  • NIE importuj modułu najwyższego poziomu await z wielu modułów równorzędnych jednocześnie (np. nie importuj statycznie ./conditionally-load-polyfill.js zarówno w a.js, jak i w b.js). Chociaż Safari 27 naprawia moduł ładujący WebKit, aby obsługiwać jednoczesne importowanie modułów najwyższego poziomu await zgodnie ze specyfikacją, Safari 15–26.6 nie działa, gdy wiele modułów jednocześnie importuje moduł zawierający await najwyższego poziomu (patrz zgodność przeglądarki MDN).

Zamiast tego wybierz jeden z 2 wzorców w zależności od struktury modułu:

  1. Moduł pojedynczego wpisu (pokazany na ilustracji main.js powyżej): jeśli moduł wpisu bezpośrednio uruchamia zależną logikę inicjowania, zaimportuj statycznie ./conditionally-load-polyfill.js u góry tego modułu i uruchom kod w jego treści.
  2. Uruchamianie oddzielnych modułów aplikacji: jeśli moduł wejściowy przekazuje zadania do modułów aplikacji niższego poziomu (takich jak ./app.js), połącz dynamiczne wywołania import() z wywołaniami najwyższego poziomu await, aby moduł ./app.js był pobierany i oceniany dopiero po rozwiązaniu modułu polyfill:
// 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');

Strategie zastępcze

Podstawowa funkcja await najwyższego poziomu jest obsługiwana w najpopularniejszych przeglądarkach od 2021 r. (Chrome 89, Edge 89, Firefox 89 i Safari 15), ale pełną zgodność ze specyfikacją osiągnęła dopiero w Safari 27 (zobacz zgodność przeglądarek MDN).

  • Safari 15–26.6: jeśli zastosujesz wzorzec pojedynczego punktu wejścia opisany w poprzedniej sekcji, dzięki czemu wiele modułów podrzędnych nie będzie jednocześnie importować modułu await najwyższego poziomu, możesz bezpiecznie polegać na module await najwyższego poziomu bez dodatkowego rozwiązania zastępczego.
  • Starsze przeglądarki (sprzed 2021 r.): jeśli Twoja aplikacja musi obsługiwać przeglądarki bez obsługi await na najwyższym poziomie, unikaj await na najwyższym poziomie i opakuj warunkową orkiestrację import() w standardową funkcję async lub łańcuch obietnic .then().