Блокуюча операція
Блокуюча операція - це задача, яка тримає єдиний потік JavaScript і зупиняє виконання всього іншого коду. Поки вона триває, event loop (цикл подій) чекає, тому інтерфейс «зависає», кнопки не натискаються, а анімації та мережеві запити не обробляються.
Теорія
TL;DR
- JavaScript працює в одному потоці, у стеку викликів (call stack) одночасно може бути лише одна задача.
- Довгий синхронний код займає весь потік, решта задач чекає.
- Event loop не може взяти нічого з черги, доки стек не звільниться.
setTimeout(..., 0)не рятує: колбек стає в чергу, але не виконується під час блокування.- Рішення: розбивати важкі задачі на частини, виносити обчислення у Web Workers, у Node.js брати асинхронні API замість
*Sync.
Швидкий приклад
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');Вивід:
A
(pause of 3 seconds)
BПоки виконується while, JavaScript не може робити нічого іншого: не обробляє кліки, не запускає таймери, не оновлює інтерфейс. Це і є blocking.
Чому це відбувається
JavaScript працює в одному потоці (single thread): в один момент часу у стеку викликів (call stack) може виконуватись лише одна задача.
Коли туди потрапляє довгий код, він займає весь потік, і решта задач, зокрема асинхронних, чекає, доки він завершиться. Навіть якщо у вас стоїть setTimeout, він не виконається, доки стек не звільниться.
Схематично:
+----------------------+
| Call Stack | <- runs the blocking operation
| (busy for a long |
| time) |
+----------------------+
|
Event loop waits
Callback queue waitsЖодна асинхронна задача не може потрапити в стек, доки блокуюча не завершиться.
Що стається з асинхронним кодом під час блокування
console.log('1');
setTimeout(() => console.log('2'), 0);
const start = Date.now();
while (Date.now() - start < 3000) {} // blocking loop, 3 seconds
console.log('3');Вивід:
1
(pause of 3 seconds)
3
2setTimeout(..., 0) не допоміг: його колбек потрапив у чергу, але не міг виконатися, доки стек не звільнився після while.
Типові блокуючі операції
| Операція | Чому блокує |
|---|---|
Довгий while / for | займає процесор |
| Великі обчислення (рекурсія, сортування, криптографія) | блокують потік |
alert(), prompt() | зупиняють виконання скрипта |
sync-версії методів Node.js (fs.readFileSync) | чекають на результат |
Довгі перетворення JSON (JSON.stringify(hugeObject)) | виконуються синхронно |
Як уникнути блокування
Розбивати великі задачі на частини (через setTimeout або requestIdleCallback):
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 для важких обчислень в окремому потоці:
// 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код усе одно виконується в головному потоці.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.