await верхнего уровня позволяет модулям действовать как асинхронные функции, то есть они могут приостанавливать выполнение модуля, чтобы дождаться промисов. Это очень удобно для условной загрузки асинхронных зависимостей, таких как полифилы или тяжелые вторичные библиотеки, только когда это требуется браузеру.
Используя 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из нескольких дочерних модулей одновременно (например, не используйте статичный импорт./conditionally-load-polyfill.jsвa.jsиb.js). Хотя в Safari 27 загрузчик модулей WebKit был исправлен и теперь поддерживает одновременный импорт модулей верхнего уровняawait, в Safari 15–26.6 при одновременном импорте модуля, содержащегоawaitверхнего уровня, возникает ошибка (см. совместимость браузеров в MDN).
Вместо этого выберите один из двух шаблонов в зависимости от структуры модуля:
- Модуль с одной точкой входа (показан на иллюстрации
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().