Чим "defer" відрізняється від "async" у <script>?
Проблема, яку вирішують defer і async
Коли браузер зустрічає тег:
<script src="script.js"></script>за умовчанням він:
- Зупиняє парсинг HTML,
- Завантажує
script.js, - Виконує його,
- Тільки потім продовжує будувати DOM.
Це блокує рендер сторінки -> користувач бачить "порожній екран", поки JS не завантажиться.
Тому з'явилися атрибути defer і async, щоб не блокувати HTML-парсинг.
defer
<script src="script.js" defer></script>Як це працює:
- Скрипт завантажується паралельно з HTML (асинхронно).
- Але виконується тільки після повного парсингу HTML -
тобто коли DOM готовий, але до події
DOMContentLoaded. - Скрипти з
deferвиконуються у порядку, у якому вони вказані в HTML.
Чудово підходить для "основних" скриптів застосунку (наприклад, main.js, vendor.js).
Приклад:
<script src="a.js" defer></script>
<script src="b.js" defer></script>Спочатку завантажаться обидва паралельно,
але виконаються строго по порядку: a.js -> b.js після побудови DOM.
async
<script src="script.js" async></script>Як це працює:
- Скрипт завантажується паралельно з HTML (як
defer), - Але виконується одразу після завантаження, навіть якщо HTML ще не допарсено.
- Скрипти з
asyncвиконуються у непередбачуваному порядку,
- залежно від того, який швидше завантажиться.
Підходить для незалежних скриптів (наприклад, аналітика, реклама, метрики).
Приклад:
<script src="a.js" async></script>
<script src="b.js" async></script>b.js може виконатися раніше, якщо завантажився швидше,
навіть якщо в HTML він був нижче.
Порівняння defer і async
| Властивість | defer | async |
|---|---|---|
| Завантаження скрипта | Асинхронно (паралельно з HTML) | Асинхронно |
| Виконання | Після повного парсингу HTML | Одразу після завантаження |
| Порядок виконання | Зберігається | Не гарантується |
| Блокує HTML-парсинг | Ні | Ні |
Подія DOMContentLoaded | Чекає виконання всіх defer-скриптів | Не чекає |
| Типові сценарії | Основна логіка сайту | Зовнішні трекери, реклама |
Приклад візуально
<!-- HTML парситься... -->
<script src="main.js" defer></script>
<script src="analytics.js" async></script>Процес:
| Етап | Що відбувається |
|---|---|
| 1 | Браузер починає парсити HTML |
| 2 | Одночасно завантажує обидва скрипти |
| 3 | analytics.js (async) завантажується швидше -> виконується одразу |
| 4 | HTML парситься до кінця |
| 5 | Потім виконується main.js (defer) |
| 6 | Потім спрацьовує DOMContentLoaded |
Висновки
| Атрибут | Коли використовувати |
|---|---|
defer | Для основного JS-коду, який залежить від DOM (наприклад, ініціалізація інтерфейсу) |
async | Для незалежних скриптів (аналітика, метрики, реклама) |
| Без атрибутів | Тільки якщо потрібно заблокувати парсинг HTML (рідко потрібно) |
Порада
Якщо ти використовуєш сучасний bundler (Next.js, Vite, Webpack) -
він сам ставить defer для <script> у head,
тому в сучасних застосунках це вже best practice за умовчанням.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.