Скрипты всегда блокирующие или бывают не блокирующие
Что проверяют
Понимаете ли вы, что делает парсер браузера, встретив <script>, и умеете ли выбирать между defer, async и модулем осознанно, а не по привычке. Заодно смотрят, отличаете ли вы блокировку парсинга от блокировки главного потока: это разные проблемы с разными решениями.
Как отвечать
Обычный <script src> блокирующий: парсер останавливается, ждёт загрузку и выполнение файла, и только потом строит DOM дальше. Не блокирующим скрипт делает атрибут:
defer: качается параллельно с разбором разметки, выполняется после построения DOM и передDOMContentLoaded, порядок между такими скриптами сохраняется;async: качается параллельно, выполняется сразу как скачался, прерывая разбор в произвольный момент; порядок не гарантирован, поэтому подходит только независимым скриптам вроде счётчиков;type="module": откладывается по умолчанию, какdefer, а вместе сasyncведёт себя как обычный асинхронный скрипт.
Два уточнения, которые отличают уверенный ответ. Инлайновый скрипт без src блокирующий всегда, атрибуты на нём игнорируются. И любой скрипт ждёт ещё не применённые стили, если тег стилей стоит выше: CSS блокирует выполнение JavaScript.
<script src="app.js" defer></script>
<script src="counter.js" async></script>Практический вывод: код приложения грузим с defer или модулем, независимые сторонние скрипты с async, крошечный критичный кусок оставляем инлайном сознательно.
Где спотыкаются
Советуют «всегда грузить асинхронно». С async два скрипта, где второй зависит от первого, сломаются случайным порядком, и баг будет плавающим.
Второе: думают, что defer и async спасают от подвисаний. Выполнение всё равно идёт в главном потоке, и тяжёлый разбор данных подвесит интерфейс независимо от атрибута; лечится делением работы на части или воркером.
Третье: путают блокировку разбора и блокировку отрисовки. Скрипт в конце body уже не мешает построить DOM, но метрики всё равно портит, потому что конкурирует за поток с первой отрисовкой.