Блокуючий і неблокуючий код
Що таке блокуючий і неблокуючий код
| Термін | Що означає |
|---|---|
| Блокуючий код (blocking code) | Виконання програми зупиняється, поки операція не завершиться |
| Неблокуючий код (non-blocking code) | Програма продовжує виконання, не чекаючи завершення операції |
1. Блокуючий код
Програма чекає, поки операція (наприклад, читання файлу чи запит до БД) закінчиться, перш ніж іти далі.
Приклад (синхронне читання файлу в Node.js):
const fs = require('fs');
console.log('1. Початок');
const data = fs.readFileSync('file.txt', 'utf8'); // блокуючий виклик
console.log('2. Файл прочитано');
console.log('3. Кінець');Вивід:
1. Початок
2. Файл прочитано
3. КінецьПоки fs.readFileSync читає файл, весь потік заблокований - Node.js не може обробити інші запити чи події.
2. Неблокуючий код
Програма не чекає - вона запускає операцію й одразу йде далі. Коли операція завершиться, результат повертається через callback, Promise або async/await.
Приклад (асинхронне читання файлу):
const fs = require('fs');
console.log('1. Початок');
fs.readFile('file.txt', 'utf8', (err, data) => {
console.log('2. Файл прочитано');
});
console.log('3. Кінець');Вивід:
1. Початок
3. Кінець
2. Файл прочитаноТут fs.readFile виконується у фоні (через libuv),
а основний потік не блокується й продовжує роботу.
Коли файл готовий - callback потрапляє в чергу подій (event loop).
3. Що робить код блокуючим
Будь-яка операція, яка чекає завершення введення-виведення (I/O) в основному потоці, - блокуюча:
fs.readFileSync(),fs.writeFileSync()JSON.parse()на величезних даних- складні обчислення (наприклад, цикл на мільйони ітерацій)
- синхронні мережеві запити (якби вони були в Node.js)
Проблема:
В однопоточному Node.js одна блокуюча ділянка коду може "заморозити" весь сервер.
4. Як Node.js робить код неблокуючим
Node.js реалізує неблокуючу модель введення-виведення (non-blocking I/O) за допомогою:
- event loop (циклу подій),
- libuv (бібліотеки, що створює фоновий пул потоків).
Принцип:
- JS-код запускає операцію (наприклад, читання файлу).
- Вона передається в libuv → виконується в іншому потоці.
- Основний потік продовжує роботу.
- Коли операція завершена → callback/Promise потрапляє назад в event loop.
Аналогія
Уяви кав'ярню:
| Сценарій | Що відбувається |
|---|---|
| Блокуючий | Бариста бере замовлення, чекає, поки звариться кава, і лише потім приймає наступне замовлення. |
| Неблокуючий | Бариста бере замовлення, віддає його в роботу й одразу приймає наступне. Коли кава готова - просто віддає клієнту. |
Результат: обслуговано більше клієнтів, ніхто не чекає марно.
5. Приклад на практиці - вебсервер
Блокуючий сервер
const http = require('http');
const fs = require('fs');
http.createServer((req, res) => {
const data = fs.readFileSync('data.json'); // блокує потік!
res.end(data);
}).listen(3000);Один запит "зависне" - решта чекатиме.
Неблокуючий сервер
const http = require('http');
const fs = require('fs');
http.createServer((req, res) => {
fs.readFile('data.json', (err, data) => {
res.end(data);
});
}).listen(3000);Сервер обробляє безліч запитів одночасно, бо читання файлів виконується у фоні.
6. Порівняння в таблиці
| Критерій | Блокуючий код | Неблокуючий код |
|---|---|---|
| Модель виконання | Синхронна | Асинхронна |
| Потік виконання | Один, і він чекає | Один, але не чекає |
| Продуктивність | Повільна при I/O | Висока при I/O |
| Підходить для | Простих скриптів | Серверів, API |
| Приклад у Node.js | fs.readFileSync() | fs.readFile() |
Підсумок
- Блокуючий код - зупиняє виконання програми, поки операція не завершиться.
- Неблокуючий код - дозволяє виконувати інші задачі, поки операція виконується у фоні.
- Node.js побудований навколо неблокуючого I/O й event loop, тому може обробляти тисячі запитів на одному потоці.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.