O await de nível superior permite que os módulos atuem como funções assíncronas, ou seja, eles podem pausar a execução do módulo para aguardar promessas. Isso é extremamente útil para carregar condicionalmente dependências assíncronas, como polyfills ou bibliotecas secundárias pesadas, apenas quando exigido pelo navegador.
Ao usar o await de nível superior, é possível encapsular a lógica de carregamento condicional em um único módulo, impedindo que os módulos de consumo downstream sejam executados até que a dependência esteja totalmente carregada e pronta.
Padrão de polyfill condicional
Embora o await de nível superior possa ser usado para carregar condicionalmente qualquer dependência assíncrona que seja um módulo, um bom uso do padrão de carregamento de dependência condicional é carregar condicionalmente polyfills para navegadores que não oferecem suporte a um recurso específico. Essa abordagem encapsula a detecção de recursos e a importação dinâmica em um único módulo de dependência.
No caso a seguir, o polyfill do atributo popover é carregado condicionalmente se não estiver presente em 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();
}
Sequenciamento de importações de módulos e como evitar o bug do Safari 27
OBRIGATÓRIO: estruture o gráfico do módulo para que o módulo await de nível superior seja importado de um único ponto de entrada e seja concluído antes que os módulos downstream sejam avaliados:
- NÃO coloque importações estáticas de mesmo nível em um arquivo de entrada (por exemplo,
import './conditionally-load-polyfill.js'; import './app.js';). De acordo com a especificação de avaliação do módulo ECMAScript, oawaitde nível superior só suspende o módulo pai que o importa, não as importações estáticas de mesmo nível. Portanto, o./app.jsserá avaliado simultaneamente em todos os navegadores antes que o polyfill termine de carregar. - NÃO importe o módulo
awaitde nível superior de vários módulos irmãos simultaneamente (por exemplo, tera.jseb.jsimportando estaticamente./conditionally-load-polyfill.js). Embora o Safari 27 tenha corrigido o carregador de módulos do WebKit para oferecer suporte a importações simultâneas de módulosawaitde nível superior por especificação, o Safari 15–26.6 falha quando vários módulos importam simultaneamente um módulo que contém umawaitde nível superior (consulte a compatibilidade do navegador MDN).
Em vez disso, escolha um dos dois padrões, dependendo da estrutura do módulo:
- Módulo de entrada única (mostrado em
main.jsacima): se o módulo de entrada executar a lógica de inicialização dependente diretamente, importe./conditionally-load-polyfill.jsde forma estática na parte de cima desse módulo e execute o código no corpo dele. - Inicialização de módulos de aplicativo separados: se o módulo de entrada delegar para módulos de aplicativo downstream (como
./app.js), encadeie chamadas dinâmicas deimport()comawaitde nível superior para que./app.jsseja buscado e avaliado somente depois que o módulo de polyfill for resolvido:
// 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');
Estratégias de fallback
Embora o await básico de nível superior seja compatível com os principais navegadores desde 2021 (Chrome 89, Edge 89, Firefox 89 e Safari 15), ele só alcançou a conformidade total com as especificações no Safari 27 (consulte a compatibilidade do navegador MDN).
- Safari 15–26.6: se você seguir o padrão de ponto de entrada único na seção anterior para que vários módulos irmãos não importem simultaneamente o módulo
awaitde nível superior, poderá confiar com segurança noawaitde nível superior sem um substituto adicional. - Navegadores legados (anteriores a 2021): se o aplicativo precisar ser compatível com navegadores sem suporte a
awaitde nível superior, evite oawaitde nível superior e envolva a orquestração condicional deimport()em uma funçãoasyncpadrão ou em uma cadeia de promessas.then().