Скрипты — атрибуты async и defer
23.05.2022
Теги: Frontend • JavaScript • Web-разработка • Документ • Теория
Когда браузер загружает HTML и доходит до тега <script>…<script>
, он не может продолжать строить DOM. Он должен сначала выполнить скрипт. То же самое происходит и с внешними скриптами <script src="…"></script>
— браузер должен подождать, пока загрузится скрипт, выполнить его, и только затем обработать остальную страницу.
Это ведёт к двум важным проблемам:
- Скрипты не видят DOM-элементы ниже себя, поэтому к ним нельзя добавить обработчики и т.д.
- Если вверху страницы объёмный скрипт, он «блокирует» страницу на время загрузки и выполнения.
Конечно, есть пути, как это обойти. Например, мы можем поместить скрипт внизу страницы. Тогда он сможет видеть элементы над ним и не будет препятствовать отображению содержимого страницы.
<body> <header>...</header> <main>...</main> <footer>...</footer> <script src="..."></script> </body>
Но это решение далеко от идеального. Например, браузер замечает скрипт (и может начать загружать его) только после того, как он полностью загрузил HTML-документ. В случае с длинными HTML-страницами это может создать заметную задержку.
К счастью, есть два атрибута тега <script>
, которые решают нашу проблему — defer
и async
.
defer
Атрибут defer
сообщает браузеру, что он должен продолжать обрабатывать страницу и загружать скрипт в фоновом режиме, а затем запустить этот скрипт, когда DOM дерево будет полностью построено. Важный момент — скрипт с атрибутом defer
всегда выполняются, когда дерево DOM готово, но до события DOMContentLoaded
.
Отложенные с помощью defer
скрипты сохраняют порядок относительно друг друга, как и обычные скрипты. Поэтому, если сначала в html-коде идет большой скрипт, а затем маленький — то последний будет ждать окончания загрузки и выполнения первого. Спецификация требует последовательного выполнения скриптов согласно порядку в документе.
<script defer src="large.js"></script> <script defer src="small.js"></script>
async
Атрибут async
означает, что скрипт абсолютно независим:
- Содержимое страницы отображается сразу же —
async
его не блокирует. DOMContentLoaded
может произойти как до, так и послеasync
, никаких гарантий нет.- Асинхронные скрипты не ждут друг друга. Меньший скрипт
small.js
идёт вторым, но скорее всего загрузится раньшеlarge.js
, поэтому и запустится первым. То есть, скрипты выполняются в порядке загрузки.
Асинхронные скрипты очень полезны для добавления на страницу сторонних скриптов — счётчиков, рекламы и т.д. Они не зависят от наших скриптов, и мы тоже не должны ждать их.
<!-- Типичное подключение скрипта Google Analytics --> <script async src="https://google-analytics.com/analytics.js"></script>
Динамически загружаемые скрипты
Мы можем также добавить скрипт и динамически, с помощью js-кода:
let script = document.createElement('script'); script.src = 'large.js'; document.body.append(script);
Динамически загружаемый скрипт по умолчанию ведет себя как «async» — начинает загружаться, как только он будет добавлен в документ:
- Сам скрипт никого не ждет, и его никто не ждёт
- Если первым загрузился — первым и запустился
Мы можем изменить относительный порядок скриптов с «первый загрузился — первый выполнился» на порядок, в котором они идут в документе (как в обычных скриптах) с помощью явной установки свойства async
в false
.
function loadScript(src) { let script = document.createElement('script'); script.src = src; script.async = false; document.body.append(script); } // large.js запускается первым, так как async=false loadScript('large.js'); loadScript('small.js');
Поиск: Frontend • JavaScript • Web-разработка • Документ • Теория