Неблокуючий код
Неблокуючий код, це код, який не зупиняє виконання потоку, а делегує довгі операції (I/O, мережа, таймери, файли) і продовжує виконувати інші задачі. Його протилежність, блокуючий код, не відпускає основний потік (main thread), доки не завершиться.
Теорія
TL;DR
- Блокуючий код тримає main thread: поки він працює, немає ні обробки кліків, ні перемальовування інтерфейсу, ні наступних функцій.
- Неблокуючий код віддає довгу операцію назовні і одразу йде далі, а результат отримує пізніше через колбек чи промайс.
- Це важливо, бо JavaScript однопотоковий: якщо один шматок коду «завис», решта чекає.
- Механіка: асинхронні API (
setTimeout,fetch,async/await), зовнішні Web API або пул потоків libuv, плюс event loop, який повертає результат. - Типові неблокуючі операції: таймери, мережа, файли в Node.js, запити до БД, обробники подій, промайси.
- Неблокуючий не дорівнює паралельний: рушій виконує один колбек за раз, просто не простоює під час очікування.
Швидкий приклад
console.log('start');
setTimeout(() => {
console.log('one second passed');
}, 1000);
console.log('end');
// start
// end
// one second passedПотік не був заблокований: JS вільно обробляв інші події, поки таймер «працював у фоні».
Що таке блокуючий код
Блокуючий код не відпускає основний потік, поки не завершиться. Поки операція виконується, JavaScript не може робити нічого іншого.
// Імітація довгого розрахунку
function heavyCalculation() {
const start = Date.now();
while (Date.now() - start < 3000) {} // чекаємо 3 секунди
console.log('calculation finished');
}
console.log('start');
heavyCalculation();
console.log('end');Результат:
start
(зависає на 3 секунди)
calculation finished
endУсе стало на 3 секунди: інтерфейс «замерз», сервер не відповідає. Це і є blocking code.
Що таке неблокуючий код
Неблокуючий варіант тієї самої затримки:
console.log('start');
setTimeout(() => {
console.log('one second passed');
}, 1000);
console.log('end');Що відбувається:
- JS викликає
setTimeoutі передає задачу браузеру (Web API). - Виконання продовжується,
'end'друкується одразу. - Через 1 секунду колбек повертається у чергу задач і виконується.
Результат:
start
end
one second passedЧому це важливо в JavaScript
JavaScript однопотоковий, тобто:
- у кожен момент часу виконується лише один шматок коду;
- якщо один шматок «завис», решта чекає.
Щоб уникнути «зависань», JS використовує:
- асинхронність (
setTimeout,fetch,async/await); - зовнішні API (Web APIs або пул потоків libuv);
- event loop, щоб повернути результат, коли він готовий.
Приклади неблокуючих операцій:
| Тип операції | Приклад | Що робить |
|---|---|---|
| Таймери | setTimeout, setInterval | виконуються пізніше |
| Мережа | fetch, XMLHttpRequest | чекають на відповідь сервера у фоні |
| Файли (Node.js) | fs.readFile | читають диск асинхронно |
| Запити до БД | db.query | чекають на результат без блокування |
| Обробники подій | addEventListener('click') | спрацьовують за подією |
| Промайси | Promise, async/await | відкладають виконання до завершення операції |
Різниця на прикладі Node.js
Блокуючий варіант:
const fs = require('fs');
console.log('reading the file...');
const data = fs.readFileSync('large.txt', 'utf8'); // блокує потік
console.log('file read:', data.length);Поки файл читається, сервер не обслуговує інші запити.
Неблокуючий варіант:
const fs = require('fs');
console.log('reading the file...');
fs.readFile('large.txt', 'utf8', (err, data) => {
console.log('file read:', data.length);
});
console.log('other code keeps running...');Node.js віддає задачу системі (I/O) і продовжує виконання. Коли файл готовий, колбек повертається через event loop.
Чому «неблокуючий» не дорівнює «паралельний»
Це часта помилка на співбесідах. Неблокуючий код не означає, що все працює паралельно. JS так само виконує один колбек за раз, але не витрачає час даремно, очікуючи на I/O. У цей час рушій обробляє інші задачі, тому й виникає враження паралельності.
| Тип | Що робить | Приклад | Наслідок |
|---|---|---|---|
| Блокуючий | Зупиняє потік, доки не завершиться | fs.readFileSync() | Зависає UI або сервер |
| Неблокуючий | Делегує задачу, продовжує роботу | fs.readFile(), fetch() | Висока чутливість інтерфейсу |
Підсумок: неблокуючий код, це спосіб писати програми, де довгі операції (I/O, мережа, таймери) виконуються асинхронно, не заважаючи основній логіці і не «заморожуючи» потік. Він лежить в основі асинхронності JS (колбеки, промайси, async/await), продуктивності Node.js і плавності інтерфейсів у браузері.
Типові помилки
- Вважати
asyncфункцію автоматично неблокуючою. Важкий цикл усерединіasyncфункції блокує потік так само, як звичайний. - Використовувати синхронні варіанти API на сервері:
fs.readFileSync,crypto.pbkdf2Sync,zlib.gzipSyncзупиняють обробку всіх запитів. - Плутати неблокуючість із паралельністю і чекати, що два
awaitпідряд виконаються одночасно. Для справжньої одночасності потрібенPromise.all. - Думати, що затримка в
setTimeoutточна. Це мінімальна затримка: колбек чекатиме, поки стек не спорожніє. - Забувати про CPU-bound роботу: винести її з потоку можуть лише Web Workers, Worker Threads або нативний код, а не асинхронний синтаксис.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.