Dipendenze asincrone condizionali

Funzionalità web: Await di primo livello

await di primo livello consente ai moduli di agire come funzioni asincrone, il che significa che possono mettere in pausa l'esecuzione del modulo per attendere le promise. Questo è estremamente utile per caricare in modo condizionale le dipendenze asincrone, come i polyfill o le librerie secondarie pesanti, solo quando richiesto dal browser.

Utilizzando l'await di primo livello, puoi incapsulare la logica di caricamento condizionale all'interno di un singolo modulo, impedendo di fatto l'esecuzione dei moduli consumer downstream fino a quando la dipendenza non è completamente caricata e pronta.

Pattern di polyfill condizionale

Sebbene l'istruzione await di primo livello possa essere utilizzata per caricare in modo condizionale qualsiasi dipendenza asincrona che sia un modulo, un buon utilizzo del pattern di caricamento condizionale delle dipendenze è il caricamento condizionale dei polyfill per i browser che non supportano una funzionalità specifica. Questo approccio incapsula il rilevamento delle funzionalità e l'importazione dinamica all'interno di un singolo modulo di dipendenza.

Nel seguente caso, il polyfill dell'attributo popover viene caricato in modo condizionale se non è presente su 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();
}

Sequencing module imports and avoiding the pre-Safari 27 bug

OBBLIGATORIO: devi strutturare il grafico del modulo in modo che il modulo await di primo livello venga importato da un singolo punto di ingresso e venga completato prima della valutazione dei moduli downstream:

  • NON inserire importazioni statiche di elementi di pari livello in un file di ingresso (ad esempio, import './conditionally-load-polyfill.js'; import './app.js';). In base alla specifica di valutazione del modulo ECMAScript, await di primo livello sospende solo il modulo principale che lo importa, non le importazioni statiche di elementi di pari livello, pertanto ./app.js verrà valutato contemporaneamente in tutti i browser prima che il polyfill termini il caricamento.
  • NON importare il modulo await di primo livello da più moduli di pari livello contemporaneamente (ad esempio, se sia a.js che b.js importano staticamente ./conditionally-load-polyfill.js). Sebbene Safari 27 abbia corretto il caricatore di moduli di WebKit per supportare le importazioni simultanee di moduli await di primo livello per specifica, Safari 15-26.6 non riesce quando più moduli importano contemporaneamente un modulo contenente un await di primo livello (consulta la compatibilità del browser MDN).

Scegli invece uno dei due pattern a seconda della struttura del modulo:

  1. Modulo di ingresso singolo (mostrato in main.js sopra): se il modulo di ingresso esegue direttamente la logica di inizializzazione dipendente, importa staticamente ./conditionally-load-polyfill.js nella parte superiore del modulo ed esegui il codice nel corpo.
  2. Bootstrapping di moduli dell'applicazione separati: se il modulo di ingresso delega ai moduli dell'applicazione downstream (come ./app.js), concatena le chiamate import() dinamiche con await di primo livello in modo che ./app.js venga recuperato e valutato solo dopo la risoluzione del modulo 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 di riserva

Sebbene l'elemento await di primo livello di base sia supportato dai principali browser dal 2021 (Chrome 89, Edge 89, Firefox 89 e Safari 15), ha raggiunto la piena conformità alle specifiche solo in Safari 27 (consulta la compatibilità del browser MDN).

  • Safari 15-26.6: se segui il pattern del singolo entry point nella sezione precedente in modo che più moduli di pari livello non importino contemporaneamente il modulo await di primo livello, puoi fare affidamento in sicurezza su await di primo livello senza un ulteriore fallback.
  • Browser legacy (precedenti al 2021): se la tua applicazione deve supportare browser senza alcun supporto await di primo livello, evita await di primo livello e racchiudi l'orchestrazione import() condizionale in una funzione async standard o in una catena di promise .then().