Пошук уроків, статей та іншого контенту
Пояснимо роль Call Stack, Callback Queue та правила переміщення задач між ними.
JavaScript у Node.js виконує синхронний код послідовно, рядок за рядком. Для керування виконанням використовуються:
Call Stack — стек викликів функцій, які виконуються прямо зараз.
Callback Queue — черга callback-функцій, готових до виконання.
Event Loop — механізм, який перевіряє стек і переносить готові callback-функції з черги до стека.
Спрощена схема:
Call Stack → виконання JavaScript-коду
Callback Queue → очікування callback-функцій
Event Loop → переносить callback у Call Stack,
коли Call Stack порожнійCall Stack — це структура даних за принципом LIFO: остання додана функція буде виконана першою.
Коли викликається функція:
Вона додається до Call Stack.
Виконується її код.
Після завершення функція видаляється зі стека.
function first() {
console.log("first");
}
function second() {
console.log("second");
}
first();
second();Порядок роботи стека:
1. Виклик first()
2. first додається до Call Stack
3. Виводиться "first"
4. first видаляється зі стека
5. Виклик second()
6. second додається до Call Stack
7. Виводиться "second"
8. second видаляється зі стекаРезультат:
first
secondЯкщо одна функція викликає іншу, нова функція додається поверх попередньої.
function third() {
console.log("third");
}
function second() {
third();
console.log("second");
}
function first() {
second();
console.log("first");
}
first();Порядок виконання:
first() → second() → third()Але результат буде таким:
third
second
firstПоки third не завершиться, second не може продовжити виконання. Поки second не завершиться, first також не може продовжити виконання.
Callback — це функція, передана іншій функції як аргумент, щоб її можна було викликати пізніше.
function processUser(name, callback) {
console.log(`Обробка користувача: ${name}`);
callback();
}
processUser("Oksana", function () {
console.log("Користувача оброблено");
});У цьому прикладі анонімна функція є callback-функцією. Вона викликається після основної операції всередині processUser.
Callback може бути синхронним або асинхронним. Якщо callback викликається одразу, він виконується через Call Stack:
function runCallback(callback) {
callback();
}
runCallback(() => {
console.log("Callback виконано");
});
console.log("Кінець програми");Результат:
Callback виконано
Кінець програмиCallback Queue — це черга callback-функцій, які вже готові до виконання, але поки не можуть потрапити в Call Stack.
Це відбувається, коли JavaScript запускає асинхронну операцію, наприклад таймер:
setTimeout(() => {
console.log("Таймер завершився");
}, 0);Callback таймера не виконується миттєво. Спочатку він очікує завершення затримки, а потім потрапляє до Callback Queue.
Важливо: setTimeout(..., 0) не означає «виконати прямо зараз». Число 0 означає, що callback може стати готовим не раніше цього моменту.
Event Loop постійно перевіряє:
Чи є ще функції в Call Stack.
Чи є готові callback-функції в Callback Queue.
Якщо Call Stack порожній, Event Loop переносить callback із черги до стека.
Callback не може бути виконаний, поки в Call Stack залишається синхронний код.
Розглянемо приклад:
console.log("Початок");
setTimeout(() => {
console.log("Callback таймера");
}, 0);
console.log("Кінець");Результат:
Початок
Кінець
Callback таймераПослідовність виконання:
console.log("Початок") виконується через Call Stack.
setTimeout реєструє таймер.
Callback таймера не виконується одразу, а очікує в Callback Queue.
console.log("Кінець") виконується синхронно.
Синхронний код завершується, Call Stack стає порожнім.
Event Loop переносить callback таймера в Call Stack.
Виконується console.log("Callback таймера").
Асинхронна операція не зупиняє виконання наступних синхронних інструкцій.
console.log("1");
setTimeout(() => {
console.log("2");
}, 1000);
console.log("3");Результат:
1
3
2Після запуску таймера JavaScript одразу переходить до наступного рядка. Приблизно через одну секунду callback стане готовим, але буде виконаний лише тоді, коли Call Stack буде порожнім.
Поки Call Stack зайнятий довгою синхронною функцією, callback із Callback Queue не може виконатися.
console.log("Початок");
setTimeout(() => {
console.log("Callback таймера");
}, 0);
for (let i = 0; i < 1_000_000_000; i++) {
// Імітація довгої синхронної операції
}
console.log("Кінець");Результат буде таким:
Початок
Кінець
Callback таймераНавіть якщо таймер має затримку 0, його callback дочекається завершення циклу. Причина — Call Stack зайнятий виконанням циклу.
Тому довгі синхронні операції можуть:
затримувати таймери;
блокувати обробку подій;
погіршувати швидкість реагування програми.
Запустити приклад можна командою:
node app.jsВміст app.js:
console.log("1. Початок програми");
setTimeout(() => {
console.log("4. Callback таймера");
}, 0);
function showMessage() {
console.log("2. Виконання синхронної функції");
}
showMessage();
console.log("3. Кінець синхронного коду");Результат:
1. Початок програми
2. Виконання синхронної функції
3. Кінець синхронного коду
4. Callback таймераПояснення:
Перший console.log одразу потрапляє в Call Stack.
Callback setTimeout очікує в Callback Queue.
showMessage() виконується синхронно.
Наступний console.log також виконується синхронно.
Після завершення синхронного коду Event Loop переносить callback таймера в Call Stack.
setTimeout(..., 0) виконається першимsetTimeout(() => {
console.log("Пізніше");
}, 0);
console.log("Зараз");Правильний результат:
Зараз
ПізнішеТаймер додає callback до черги, а синхронний console.log виконується одразу.
Callback Queue не виконує функції самостійно. Вона лише зберігає готові callback-функції.
Виконання відбувається після перенесення callback до Call Stack через Event Loop.
setTimeout(callback, 1000);Це означає: callback не буде готовим раніше ніж приблизно через одну секунду. Але він може виконатися пізніше, якщо Call Stack зайнятий або Node.js обробляє інші операції.
Будь-який довгий синхронний код затримує виконання callback-функцій. Асинхронний API не може змусити callback виконатися, поки стек зайнятий.
Call Stack містить функції, які виконуються зараз.
Функції у стеку виконуються послідовно.
Callback Queue містить callback-функції, готові до виконання.
Event Loop переносить callback із черги в Call Stack.
Callback не виконується, поки Call Stack не стане порожнім.
setTimeout(..., 0) не виконує callback миттєво.
Довгий синхронний код блокує Call Stack і затримує callback-функції.