Является ли JavaScript однопоточным или многопоточным языком
Что проверяют
Понимаете ли вы, где заканчивается язык и начинается среда выполнения. Ответ «однопоточный» без оговорок засчитывают наполовину. Следом почти всегда просят объяснить event loop и порядок срабатывания setTimeout, Promise.then и кода после await.
Как отвечать
Сам язык однопоточный: у каждого агента (вкладка, воркер) один стек вызовов, и ваш код в нём выполняется строго по очереди. Многопоточна среда, то есть браузер или Node.js.
- event loop: берёт из очереди задачу (
setTimeout, клик, сообщение изpostMessage), выполняет её до конца, затем опустошает всю очередь микрозадач (Promise.then,queueMicrotask, продолжение послеawait) и только потом даёт браузеру отрисовать кадр; - асинхронный ввод-вывод:
fetch, таймеры и чтение файлов выполняет среда вне основного потока, в JS возвращается только колбэк с результатом. Поэтому ожидание сети не блокирует страницу; - настоящая параллельность: Web Workers и
worker_threadsв Node.js. У каждого воркера свой поток, свой event loop и своя память, общаются они черезpostMessage, копируя данные или передаваяArrayBuffer; - общая память:
SharedArrayBufferиAtomics. В браузере доступны только на странице с cross-origin isolation, то есть с заголовкамиCross-Origin-Opener-PolicyиCross-Origin-Embedder-Policy.
Закончите практикой: тяжёлый расчёт в основном потоке (парсинг большого JSON, обработка картинки) замораживает интерфейс и портит INP, поэтому его режут на части или уносят в воркер.
Где спотыкаются
Говорят, что async/await выполняет код в другом потоке. Синхронный цикл внутри async-функции блокирует страницу точно так же, как без неё.
Путают асинхронность с параллельностью: запросы летят одновременно, потому что их обслуживает браузер, а не потому, что JS стал многопоточным.
Считают, что setTimeout(fn, 0) сработает раньше Promise.resolve().then(fn). Микрозадачи всегда выполняются до следующей задачи из очереди, а у таймера есть ещё и минимальная задержка.