Как работает async в скриптах: примеры и инструкции

Раздел: Веб-разработка -> Подключение скриптов на HTML-странице

Асинхронная загрузка скриптов с атрибутом async

Атрибут async для тега <script> позволяет браузеру загружать скрипт параллельно с парсингом HTML, а выполнить его сразу после получения, не дожидаясь окончания разбора документа. Это ускоряет загрузку страницы, но меняет порядок выполнения. Рассмотрим основной подход и альтернативы.

Как сделать загрузку скрипта независимой от парсинга документа?

Добавьте атрибут async к тегу <script>:

<script async src="analytics.js"></script>

Js script async (асинхронный скрипт (script async))

Браузер начнёт загрузку analytics.js сразу, не останавливая чтение HTML. Скрипт выполнится в момент, когда файл будет получен - возможно, до завершения парсинга, возможно, после. Это подходит для независимых скриптов (счётчики, виджеты).

Типичная ошибка: скрипт обращается к DOM, которого ещё нет. Решение - проверять существование элемента или оборачивать логику в событие DOMContentLoaded внутри самого скрипта.

Проблема: потеря порядка выполнения, если нужно несколько скриптов с зависимостями. Async не гарантирует последовательность.

Как загрузить скрипт после полной загрузки DOM, сохраняя порядок?

Используйте атрибут defer:

<script defer src="jquery.js"></script>
<script defer src="app.js"></script>

Скрипты загружаются параллельно, но выполняются строго по порядку после того, как весь HTML разобран, перед событием DOMContentLoaded.

Ошибка: путать async и defer. Defer подходит, когда скрипты зависят друг от друга или от DOM.

Как управлять загрузкой скрипта из JavaScript динамически?

Создайте элемент <script> и установите атрибут async в true или false:

const script = document.createElement('script');
script.src = 'widget.js';
script.async = true;
document.body.appendChild(script);

По умолчанию динамически созданные скрипты имеют async = true. Чтобы отключить, явно укажите script.async = false.

Проблема: скрипт может выполниться до того, как загрузятся другие ресурсы. Рекомендуется добавлять скрипты после события DOMContentLoaded, если они манипулируют DOM.

Как использовать async с модулями ES6?

Модульные скрипты (type="module") по умолчанию ведут себя как defer. Если добавить async, они будут выполняться сразу после загрузки, независимо от порядка:

<script type="module" async src="app.mjs"></script>

Это редко применяется, так как модули обычно содержат зависимости.

Ошибка: полагаться на порядок выполнения async-модулей - он не гарантирован.

Расширенные примеры использования async

Пример 1. Неопределённый порядок выполнения нескольких async-скриптов

Пример
<script async src="script1.js"></script>
<script async src="script2.js"></script>
<script async src="script3.js"></script>

В файлах:

Пример
// script1.js
console.log('Скрипт 1');
// script2.js
console.log('Скрипт 2');
// script3.js
console.log('Скрипт 3');

Результат может быть любым, например:

Скрипт 2
Скрипт 1
Скрипт 3

Порядок зависит от скорости загрузки каждого файла. Для зависимых скриптов используйте defer или объединяйте в один файл.

Пример 2. Async скрипт и событие DOMContentLoaded

Пример
<script>
  document.addEventListener('DOMContentLoaded', () => {
    console.log('DOM готов');
  });
</script>
<script async src="late.js"></script>
<!-- late.js: console.log('Async скрипт'); -->

Если late.js загрузится быстро, его выполнение может произойти до события DOMContentLoaded. При медленной загрузке - после. Результат:

(Возможный вариант 1)
Async скрипт
DOM готов
(Вариант 2)
DOM готов
Async скрипт

Это важно для скриптов, которые должны гарантированно работать с готовым DOM - в них нужно внутри проверять состояние документа или слушать событие.

Пример 3. Async и document.write (не делайте так)

Пример
<script async>
  document.write('<p>Новый текст</p>');
</script>

После того как HTML разобран, document.write перезаписывает весь документ. Скрипт с async может выполниться в любой момент, что приведёт к потере содержимого страницы.

Результат: пустая страница или неожиданное содержимое. Используйте innerHTML или DOM-методы.

Пример 4. Обработка ошибок загрузки async-скрипта

Пример
<script async src="https://example.com/tracker.js"
        onerror="console.error('Не удалось загрузить скрипт')"></script>

Событие onerror срабатывает, если ресурс недоступен. Это полезно для аналитики - можно попробовать альтернативный источник.

Не удалось загрузить скрипт

Пример 5. Async с IntersectionObserver для ленивой загрузки

Пример
<script async src="lazyload.js"></script>
<!-- в lazyload.js: -->
const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      entry.target.src = entry.target.dataset.src;
      observer.unobserve(entry.target);
    }
  });
});
document.querySelectorAll('img[data-src]').forEach(img => observer.observe(img));

Async скрипт выполняется быстро, но если изображений ещё нет в DOM (из-за позднего выполнения), селекторы ничего не найдут. Решение - запустить наблюдение после DOMContentLoaded внутри скрипта:

Пример
if (document.readyState === 'loading') {
  document.addEventListener('DOMContentLoaded', initObserver);
} else {
  initObserver();
}

Пример 6. Комбинация async с defer (не имеет смысла)

Если указать оба атрибута, async имеет приоритет. Браузер проигнорирует defer. Проверка:

Пример
<script async defer src="test.js"></script>

Файл test.js выполнится асинхронно, не дожидаясь окончания парсинга.

Асинхронный скрипт (script async) - comments

En
Js script async (javascript)