Как парсится и обрабатывается JavaScript в браузере
Что проверяют
Представляете ли вы путь скрипта целиком: от тега в HTML до выполнения в движке и влияния на отрисовку. Следом почти всегда спрашивают разницу между async и defer или почему тяжёлый расчёт в промисе всё равно подвешивает страницу.
Как отвечать
Разбейте ответ на три этапа.
Загрузка. HTML-парсер, встретив обычный <script src>, останавливается и ждёт загрузки и выполнения, потому что скрипт может изменить разметку через document.write. Варианты без блокировки:
defer: грузится параллельно, выполняется после разбора HTML в порядке следования, доDOMContentLoaded;async: выполняется сразу по готовности, порядок не гарантирован, подходит для независимых скриптов вроде аналитики;type="module": по умолчанию ведёт себя какdefer, зависимости подтягиваются по графу импортов.
Пока основной парсер стоит, preload scanner заранее находит ресурсы в разметке и начинает их качать.
Разбор и компиляция на примере V8. Сканер делит текст на токены, парсер строит AST. Функции, которые не вызываются сразу, разбираются лениво: сначала только проверка синтаксиса, полный разбор при первом вызове. Интерпретатор Ignition превращает AST в байткод и собирает сведения о типах. Горячий код поднимается по уровням: быстрый неоптимизирующий Sparkplug, затем Maglev, для самого горячего TurboFan. Если типы поменялись, происходит деоптимизация и возврат к байткоду. У SpiderMonkey и JavaScriptCore схема с уровнями похожая.
Выполнение. Создаётся глобальный контекст выполнения, объявления поднимаются, код идёт через стек вызовов в основном потоке. Дальше event loop: взять задачу, выполнить, выгрести все микрозадачи (колбэки промисов), при необходимости отрисовать кадр.
Где спотыкаются
Называют JavaScript интерпретируемым построчно. Современные движки компилируют код в несколько уровней прямо во время выполнения.
Путают async и defer. Для скриптов, зависящих друг от друга или от готового DOM, async даёт плавающие ошибки.
Считают, что Promise или async/await уводят работу в фон. Колбэки выполняются в том же основном потоке, поэтому долгий цикл блокирует ввод и отрисовку. Тяжёлое выносят в Web Worker или дробят на части.