Какая проблема возникает при загрузке скриптов и как атрибуты defer и async решают эту проблему
Что проверяют
Понимаете ли вы, как парсер HTML взаимодействует с загрузкой скриптов, и выбираете ли атрибут по назначению файла, а не по привычке. Дальше обычно просят разложить порядок выполнения нескольких тегов подряд и спрашивают, как в схему встраивается type="module".
Как отвечать
Начните с проблемы: встретив <script src> без атрибутов, парсер останавливается, качает файл и выполняет его, и только потом строит DOM дальше. Содержимое в это время не отрисовано, страдают FCP и LCP. Вдобавок такой скрипт ждёт применения объявленных выше стилей, потому что может запросить вычисленные значения.
defer: качается параллельно разбору документа, выполняется после того, как HTML разобран, но доDOMContentLoaded. Порядок между несколькими отложенными скриптами сохраняется по разметке;async: качается параллельно, выполняется сразу по готовности, прерывая разбор в любой момент. Порядок между несколькими такими скриптами не гарантирован: побеждает тот, кто раньше скачался;- что куда:
deferдля кода приложения, которому нужен готовый DOM и предсказуемый порядок.asyncдля независимых счётчиков и виджетов; - модули:
<script type="module">откладывается по умолчанию. Атрибутasyncработает и на встроенном модуле, а у классических скриптов оба атрибута для инлайна игнорируются; - динамическая вставка: у скрипта, созданного через
document.createElement("script"), свойствоasyncравноtrueпо умолчанию, для сохранения порядка ставятscript.async = false.
Сильный ответ добавляет соседнее: <link rel="preload"> и modulepreload для раннего скачивания, fetchpriority для приоритетов, разбиение бандла, чтобы отложенный код не оказался одним тяжёлым файлом.
Где спотыкаются
Говорят, что оба атрибута «делают загрузку асинхронной», и на этом заканчивают. Загрузка в обоих случаях параллельная, разница в моменте выполнения.
Ставят async на код, зависящий от библиотеки из соседнего тега. Получается плавающая ошибка, воспроизводящаяся через раз и только на медленной сети.
Считают, что тег перед закрывающим body равен defer. Он всё равно блокирует парсер на своём месте и выполняется раньше отложенных скриптов.