Skip to main content

Неблокуючий код

Неблокуючий код, це код, який не зупиняє виконання потоку, а делегує довгі операції (I/O, мережа, таймери, файли) і продовжує виконувати інші задачі. Його протилежність, блокуючий код, не відпускає основний потік (main thread), доки не завершиться.

Теорія

TL;DR

  • Блокуючий код тримає main thread: поки він працює, немає ні обробки кліків, ні перемальовування інтерфейсу, ні наступних функцій.
  • Неблокуючий код віддає довгу операцію назовні і одразу йде далі, а результат отримує пізніше через колбек чи промайс.
  • Це важливо, бо JavaScript однопотоковий: якщо один шматок коду «завис», решта чекає.
  • Механіка: асинхронні API (setTimeout, fetch, async/await), зовнішні Web API або пул потоків libuv, плюс event loop, який повертає результат.
  • Типові неблокуючі операції: таймери, мережа, файли в Node.js, запити до БД, обробники подій, промайси.
  • Неблокуючий не дорівнює паралельний: рушій виконує один колбек за раз, просто не простоює під час очікування.

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

javascript
console.log('start'); setTimeout(() => { console.log('one second passed'); }, 1000); console.log('end'); // start // end // one second passed

Потік не був заблокований: JS вільно обробляв інші події, поки таймер «працював у фоні».

Що таке блокуючий код

Блокуючий код не відпускає основний потік, поки не завершиться. Поки операція виконується, JavaScript не може робити нічого іншого.

javascript
// Імітація довгого розрахунку function heavyCalculation() { const start = Date.now(); while (Date.now() - start < 3000) {} // чекаємо 3 секунди console.log('calculation finished'); } console.log('start'); heavyCalculation(); console.log('end');

Результат:

javascript
start (зависає на 3 секунди) calculation finished end

Усе стало на 3 секунди: інтерфейс «замерз», сервер не відповідає. Це і є blocking code.

Що таке неблокуючий код

Неблокуючий варіант тієї самої затримки:

javascript
console.log('start'); setTimeout(() => { console.log('one second passed'); }, 1000); console.log('end');

Що відбувається:

  1. JS викликає setTimeout і передає задачу браузеру (Web API).
  2. Виконання продовжується, 'end' друкується одразу.
  3. Через 1 секунду колбек повертається у чергу задач і виконується.

Результат:

javascript
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

Блокуючий варіант:

javascript
const fs = require('fs'); console.log('reading the file...'); const data = fs.readFileSync('large.txt', 'utf8'); // блокує потік console.log('file read:', data.length);

Поки файл читається, сервер не обслуговує інші запити.

Неблокуючий варіант:

javascript
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 або нативний код, а не асинхронний синтаксис.

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

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

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