Skip to main content

Синхронний та асинхронний callback

Синхронний callback виконується одразу під час виклику основної функції, у тому самому стеку викликів, а асинхронний виконується пізніше, вже після завершення поточного коду, через event loop. Різниця не в синтаксисі колбека, а в тому, коли саме функція, що його отримала, вирішує його викликати.

Теорія

TL;DR

  • Синхронний callback виконується негайно, до завершення поточної функції, у тому самому call stack.
  • Асинхронний callback ставиться в чергу і виконується після того, як стек звільниться.
  • Приклади синхронних: array.map(fn), forEach, sort, JSON.parse з reviver.
  • Приклади асинхронних: setTimeout(fn, 0), fetch().then(fn), обробники подій.
  • setTimeout(fn, 0) не означає «зараз», він означає «якнайшвидше, але після всього синхронного коду».

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

javascript
function process(callback) { console.log('Before the callback'); callback(); // runs immediately console.log('After the callback'); } process(() => console.log('Synchronous callback'));

Вивід:

text
Before the callback Synchronous callback After the callback

Усе виконується по порядку, без затримок.

Синхронний callback

Синхронний callback виконується одразу, під час виклику основної функції, тобто в тому самому стеку викликів, ще до того як поточна функція завершиться. Він блокує подальше виконання: наступний рядок не почнеться, доки колбек не відпрацює.

javascript
const numbers = [1, 2, 3]; const doubled = numbers.map(n => { console.log('processing', n); // runs on every element, right now return n * 2; }); console.log(doubled); // [2, 4, 6] is already available on this line

Саме тому результат map доступний одразу після виклику: колбек не «планується», він просто викликається.

Асинхронний callback

Асинхронний callback викликається пізніше, після завершення поточного коду: за таймером, після запиту, після події. Він потрапляє в чергу задач, а event loop візьме його лише тоді, коли call stack буде порожній.

javascript
function processAsync(callback) { console.log('Before the callback'); setTimeout(callback, 0); // will run later console.log('After the callback'); } processAsync(() => console.log('Asynchronous callback'));

Вивід:

text
Before the callback After the callback Asynchronous callback

Колбек виконується не одразу, а після того, як стек викликів звільниться, навіть якщо затримка дорівнює нулю.

Порівняння

ТипКоли виконуєтьсяПриклад
СинхроннийОдразу, під час викликуarray.map(fn)
АсинхроннийПізніше, після основного кодуsetTimeout(fn, 0), fetch().then(fn)

Черга мікрозадач і макрозадач

Серед асинхронних колбеків теж є порядок. Колбеки Promise потрапляють у чергу мікрозадач, яку event loop спустошує одразу після поточного синхронного коду, а setTimeout дає макрозадачу і чекає наступної ітерації циклу.

javascript
console.log('1 sync'); setTimeout(() => console.log('4 macrotask'), 0); Promise.resolve().then(() => console.log('3 microtask')); console.log('2 sync'); // order: 1 sync, 2 sync, 3 microtask, 4 macrotask

Типові помилки

  • Читати результат асинхронної операції одразу після виклику функції: там ще undefined, бо колбек не встиг виконатися.
  • Вважати, що setTimeout(fn, 0) виконає функцію негайно: вона чекає на порожній call stack.
  • Ловити помилку з асинхронного колбека зовнішнім try/catch: коли колбек виконується, той блок уже завершився.
  • Ставити return у колбеку forEach і сподіватися, що це поверне значення із зовнішньої функції: forEach ігнорує результат.
  • Блокувати потік важким синхронним колбеком у циклі: сторінка перестає реагувати, бо стек не звільняється.
  • Плутати чергу мікрозадач і макрозадач: Promise.then завжди випередить setTimeout(..., 0).

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

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

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