Skip to main content

Блокуюча операція

Блокуюча операція - це задача, яка тримає єдиний потік JavaScript і зупиняє виконання всього іншого коду. Поки вона триває, event loop (цикл подій) чекає, тому інтерфейс «зависає», кнопки не натискаються, а анімації та мережеві запити не обробляються.

Теорія

TL;DR

  • JavaScript працює в одному потоці, у стеку викликів (call stack) одночасно може бути лише одна задача.
  • Довгий синхронний код займає весь потік, решта задач чекає.
  • Event loop не може взяти нічого з черги, доки стек не звільниться.
  • setTimeout(..., 0) не рятує: колбек стає в чергу, але не виконується під час блокування.
  • Рішення: розбивати важкі задачі на частини, виносити обчислення у Web Workers, у Node.js брати асинхронні API замість *Sync.

Швидкий приклад

javascript
console.log('A'); function block(ms) { const start = Date.now(); while (Date.now() - start < ms) {} } block(3000); // block the thread for 3 seconds console.log('B');

Вивід:

javascript
A (pause of 3 seconds) B

Поки виконується while, JavaScript не може робити нічого іншого: не обробляє кліки, не запускає таймери, не оновлює інтерфейс. Це і є blocking.

Чому це відбувається

JavaScript працює в одному потоці (single thread): в один момент часу у стеку викликів (call stack) може виконуватись лише одна задача.

Коли туди потрапляє довгий код, він займає весь потік, і решта задач, зокрема асинхронних, чекає, доки він завершиться. Навіть якщо у вас стоїть setTimeout, він не виконається, доки стек не звільниться.

Схематично:

javascript
+----------------------+ | Call Stack | <- runs the blocking operation | (busy for a long | | time) | +----------------------+ | Event loop waits Callback queue waits

Жодна асинхронна задача не може потрапити в стек, доки блокуюча не завершиться.

Що стається з асинхронним кодом під час блокування

javascript
console.log('1'); setTimeout(() => console.log('2'), 0); const start = Date.now(); while (Date.now() - start < 3000) {} // blocking loop, 3 seconds console.log('3');

Вивід:

javascript
1 (pause of 3 seconds) 3 2

setTimeout(..., 0) не допоміг: його колбек потрапив у чергу, але не міг виконатися, доки стек не звільнився після while.

Типові блокуючі операції

ОпераціяЧому блокує
Довгий while / forзаймає процесор
Великі обчислення (рекурсія, сортування, криптографія)блокують потік
alert(), prompt()зупиняють виконання скрипта
sync-версії методів Node.js (fs.readFileSync)чекають на результат
Довгі перетворення JSON (JSON.stringify(hugeObject))виконуються синхронно

Як уникнути блокування

Розбивати великі задачі на частини (через setTimeout або requestIdleCallback):

javascript
function bigTask() { for (let i = 0; i < 1e9; i++) { if (i % 1e6 === 0) console.log(i); } } setTimeout(bigTask, 0); // scheduled asynchronously, does not block the interface

Використовувати Web Workers для важких обчислень в окремому потоці:

javascript
// worker.js onmessage = (e) => { const result = e.data ** 2; postMessage(result); };

У Node.js брати fs.promises замість fs.readFileSync.

Підсумок

Уявіть, що у вас один офіціант (потік JavaScript):

  • Блокуючий код: офіціант стоїть біля плити і чекає, доки приготується страва, і нікого не обслуговує.
  • Неблокуючий код: він передає замовлення на кухню і йде до наступного столика.

JavaScript працює як другий варіант. Але якщо офіціант сам почне 10 хвилин мішати суп (CPU-heavy код), обслуговування зупиниться.

ТермінЩо означає
Блокуюча операціязадача, яка зупиняє виконання решти коду
Результатevent loop «заморожується», інтерфейс не реагує
Прикладидовгі цикли, sync-функції, alert(), великі обчислення
Як уникнутиасинхронні API, Web Workers, поділ задачі на частини

Типові помилки

  • Вважати, що setTimeout(fn, 0) робить код асинхронним «всередині»: воно лише переносить виклик на пізніше, сама функція так само заблокує потік.
  • Використовувати fs.readFileSync у HTTP-обробнику: один повільний диск зупиняє обслуговування всіх запитів.
  • Серіалізувати гігантський об'єкт через JSON.stringify у головному потоці, замість потокової обробки або воркера.
  • Плутати «довгий запит до сервера» з блокуванням: очікування мережі не блокує потік, блокують саме обчислення.
  • Думати, що await звільняє потік від важкого синхронного коду всередині функції: після await код усе одно виконується в головному потоці.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.