В классической модели браузера/Node ваш JS-код исполняется в одном потоке: один call stack, один кусок синхронного кода в момент времени. Параллельно «сами собой» крутятся I/O, таймеры, сеть — но колбэки снова попадают в очередь и выполняются тем же циклом (Event Loop).
// Пока это крутится, UI клики не обработаютсяfor (let i = 0; i < 1e9; i++) {}
Но ведь есть Web Workers?
Да — это другие потоки со своим JS-контекстом и обменом через сообщения. Основной UI-поток всё равно один. SharedArrayBuffer / Atomics — продвинутый параллелизм, не «обычный» стиль приложения.
Зачем так сделали
Проще модель для DOM: нет гонок за дерево из десяти потоков. Асинхронность достигается очередями задач, а не shared-memory multithreading в пользовательском коде по умолчанию.
Итог: JS однопоточен в смысле одного call stack приложения; «параллельность» — через Event Loop, async I/O и отдельные workers.