W3docs

Скрипты: async, defer

Атрибуты async и defer позволяют загружать JavaScript в фоне, не блокируя разбор HTML и ускоряя отображение страницы.

То, где вы размещаете <script> и как вы его загружаете, напрямую влияет на скорость появления страницы. По умолчанию скрипты могут остановить браузер в середине рендеринга; атрибуты async и defer позволяют сообщить браузеру, что JavaScript нужно скачать в фоне. В этой статье объясняется, как ведут себя обычные блокирующие скрипты, что меняют async и defer, почему модульные скрипты откладываются автоматически, и как выбрать подходящий вариант для каждого скрипта.

Как обычный скрипт блокирует разбор

<script> без async или defer блокирует рендеринг. Когда HTML-парсер встречает этот тег, он прекращает строить страницу, загружает скрипт (если указан атрибут src), выполняет его до конца и только затем возобновляет разбор остального документа:

<p>...content before script...</p>

<script src="big.js"></script> <!-- parsing pauses here until big.js downloads AND runs -->

<p>...content after script (the user can't see this yet)...</p>

Из этого следует два последствия:

  • Скрипт не видит элементы DOM, которые расположены после него, поскольку они ещё не разобраны.
  • Пока скрипт загружается, пользователь видит лишь частично построенную страницу. На медленном соединении это классическая проблема «белой страницы».

Исторически её решали, помещая скрипты в самый конец <body>. async и defer дают более чистое решение: оставьте тег в <head>, но запретите ему блокировать загрузку.

Оба атрибута async и defer действуют только на внешние скрипты — им необходим атрибут src. На встроенных блоках <script> они игнорируются.

Понимание атрибута async

Что такое атрибут async?

Когда вы добавляете атрибут async к тегу <script>, браузеру даётся команда скачать скрипт в фоне, не блокируя разбор HTML. Как только загрузка завершается, браузер приостанавливает разбор, выполняет скрипт и продолжает. Поскольку время загрузки варьируется, скрипты с async запускаются как только готовы — в непредсказуемом порядке, возможно до того, как остальной документ разобран.

Это делает async идеальным для независимых скриптов, которые не зависят от DOM или друг от друга: аналитика, реклама и прочие трекеры, работающие по принципу «выстрелил и забыл».

Пример кода: использование async

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Async Example</title>
</head>
<body>
    <h1>Testing Async</h1>
    <script async src="https://example.com/async-script.js"></script>
</body>
</html>

Преимущества и компромиссы async

  • Без блокировки: разбор HTML продолжается во время загрузки скрипта.
  • Запускается немедленно: скрипт выполняется сразу по завершении загрузки — хорошо для независимых скриптов.
  • Без гарантированного порядка: при наличии нескольких скриптов с async первым запускается тот, что загрузится быстрее. Никогда не используйте async для скриптов, зависящих друг от друга.
  • Может запуститься до готовности DOM: скрипт может выполниться до окончания разбора, поэтому не предполагайте наличие последующих элементов.

Использование атрибута defer

Что такое атрибут defer?

Атрибут defer тоже загружает скрипт в фоне без блокировки разбора. Отличие — в моменте выполнения: отложенный скрипт запускается только после полного разбора HTML-документа, прямо перед тем как срабатывает событие DOMContentLoaded. Отложенные скрипты также сохраняют порядок в документе — первый тег с defer всегда выполняется раньше второго, независимо от того, какой загрузился первым.

Это делает defer правильным выбором по умолчанию для кода приложения: весь DOM гарантированно существует, а зависимые скрипты выполняются по порядку.

Пример кода: использование defer

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Defer Example</title>
</head>
<body>
    <h1>Testing Defer</h1>
    <script defer src="https://example.com/defer-script.js"></script>
</body>
</html>

Преимущества использования defer

  • DOM готов: весь HTML-документ разобран до запуска скрипта.
  • Порядок сохраняется: скрипты выполняются в порядке их расположения в документе, что критично для скриптов, зависящих друг от друга.

Модульные скрипты откладываются по умолчанию

<script type="module"> ведёт себя как отложенный скрипт автоматически — добавлять defer не нужно. Он загружается без блокировки разбора и выполняется после разбора документа, по порядку. Чтобы модуль запустился как только он готов (поведение async), добавьте async явно:

<!-- Deferred automatically; runs after parsing, in order -->
<script type="module" src="app.js"></script>

<!-- Opt into async: runs as soon as it (and its imports) are ready -->
<script type="module" async src="tracker.js"></script>

Если вы только знакомитесь с модулями, см. Введение в модули.

async и defer в сравнении

Поведениеasyncdefer
Блокирует разбор HTML?НетНет
Когда выполняется?Сразу после загрузкиПосле разбора, до DOMContentLoaded
Порядок выполненияКто загрузился первымПорядок в документе, гарантировано
DOM полностью доступен?Не гарантированоДа
Лучше всего дляНезависимых скриптов (аналитика, реклама)Кода приложения, зависимых скриптов

Простое правило:

  • Используйте async, когда скрипт самодостаточен — не зависит от других скриптов или от DOM.
  • Используйте defer, когда скрипту нужен весь DOM или важен порядок выполнения.

Практический пример: решение о загрузке скриптов

Рассмотрим загрузку утилитарной библиотеки (например, Lodash) и вашего файла, который от неё зависит. Поскольку порядок важен, defer — правильный выбор: оба скрипта скачиваются в фоне, но выполняются последовательно после разбора:

<script defer src="https://cdn.jsdelivr.net/npm/lodash/lodash.min.js"></script>
<script defer src="script.js"></script>

Здесь lodash.min.js гарантированно выполнится раньше script.js, и оба дождутся разбора страницы. Если заменить их на async, есть риск, что script.js запустится первым и завершится с ошибкой, потому что Lodash ещё не загружен.

Для загрузки не-скриптовых ресурсов (изображений, стилей) и обработки их успешной загрузки или ошибки см. Загрузка ресурсов: onload и onerror.

Заключение

Грамотное использование атрибутов async и defer в тегах скриптов критически важно для современной веб-разработки. Понимая и правильно применяя эти атрибуты, разработчики могут обеспечить более быструю загрузку страниц и лучший пользовательский опыт. Асинхронная загрузка скриптов — это оптимизация производительности и создание эффективных веб-приложений, ориентированных на пользователя.

Практика

Практика
Для чего используются атрибуты 'async' и 'defer' в JavaScript?
Для чего используются атрибуты 'async' и 'defer' в JavaScript?
Was this page helpful?