Почему JavaScript однопоточный
Что проверяют
Понимаете ли вы историческую причину и знаете ли, что однопоточность не мешает асинхронности.
Как отвечать
Причина историческая и связана с DOM. JavaScript создавался как язык сценариев для страницы, а DOM это общее изменяемое дерево. Несколько потоков, одновременно меняющих один узел, потребовали бы блокировок в каждой операции с деревом, что означало бы гонки, взаимные блокировки и модель памяти в языке для веб-страниц. Разработчики выбрали простоту: один поток, никакой синхронизации, предсказуемый порядок выполнения.
Дальше это закрепилось: спецификация описывает цикл событий с очередью задач, и весь пользовательский код одного контекста исполняется в одном потоке вместе с отрисовкой.
Ключевое уточнение, которого обычно ждут: однопоточный это исполнение JavaScript, а не среда целиком. Сетевые запросы, таймеры, чтение файлов выполняет браузер или Node своими потоками, а в очередь возвращается только колбэк. Поэтому язык неблокирующий, будучи однопоточным: ожидание отдано среде.
Отсюда практическое следствие: заблокировать поток всё-таки можно, и тогда встанет всё, включая отрисовку и реакцию на клик. Тяжёлый синхронный цикл на секунду это секунда замершего интерфейса.
Как обходят сегодня: Web Workers в браузере и worker_threads в Node дают настоящую параллельность, но с изолированной памятью и обменом сообщениями, а не общими переменными. Разделяемая память возможна только явно, через SharedArrayBuffer и Atomics. То есть исходный принцип сохранён: общего изменяемого состояния между потоками по умолчанию нет.
Где спотыкаются
Отвечают «потому что так решили», не связывая с DOM.
Считают, что асинхронность означает многопоточность.
Не знают про Web Workers.
Думают, что setTimeout создаёт поток.