Skip to main content

Callback-функція

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

Теорія

TL;DR

  • Callback це функція-аргумент, яку викликає не ваш код, а та функція, якій ви її віддали.
  • Це можливо тому, що в JavaScript функції є значеннями першого класу: їх можна передавати й повертати.
  • Колбеки бувають синхронні (map, filter, forEach) і асинхронні (setTimeout, обробники подій, запити).
  • Передавайте посилання на функцію, а не її виклик: setTimeout(fn, 100), а не setTimeout(fn(), 100).
  • Багато вкладених колбеків перетворюються на callback hell, який лікують Promise і async/await.

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

javascript
function fetchData(callback) { setTimeout(() => { const data = 'Data from the server'; callback(data); // викликаємо передану функцію }, 1000); } fetchData(result => console.log(result)); // "Data from the server"

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

Чому це працює: функції як значення

У JavaScript функція це звичайне значення, тому її можна присвоїти змінній, покласти в масив чи передати в іншу функцію. Функцію, яка приймає або повертає іншу функцію, називають функцією вищого порядку (higher-order function), а передану функцію, callback.

javascript
const numbers = [1, 2, 3]; // map це функція вищого порядку, стрілкова функція всередині це callback const doubled = numbers.map(n => n * 2); // [2, 4, 6]

Синхронні та асинхронні колбеки

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

javascript
// синхронний: виконається до наступного рядка [1, 2, 3].forEach(n => console.log(n)); // асинхронний: виконається після всього синхронного коду setTimeout(() => console.log('later'), 0); console.log('now'); // порядок у консолі: 1, 2, 3, now, later

Обробка помилок: стиль error-first

У Node.js історично склалася домовленість: перший параметр колбека це помилка, а дані йдуть далі. Так викликач може обробити збій у тому самому місці, де отримує результат.

javascript
function loadUser(id, callback) { setTimeout(() => { if (!id) { callback(new Error('id is required')); return; } callback(null, { id, name: 'Maria' }); }, 100); } loadUser(7, (err, user) => { if (err) { console.error(err.message); return; } console.log(user.name); // Maria });

Callback hell і сучасна альтернатива

Коли одна асинхронна операція залежить від іншої, колбеки починають вкладатися один в одний, і код зсувається вправо «драбинкою». Це і називають callback hell: його важко читати й ще важче обробляти в ньому помилки.

javascript
loadUser(7, (err, user) => { loadOrders(user.id, (err, orders) => { loadItems(orders[0].id, (err, items) => { console.log(items); // вкладеність росте з кожним кроком }); }); });

Сучасне рішення це Promise і async/await: та сама послідовність записується пласко, а помилки ловляться одним try/catch.

javascript
async function showItems() { try { const user = await loadUser(7); const orders = await loadOrders(user.id); const items = await loadItems(orders[0].id); console.log(items); } catch (err) { console.error(err.message); } }

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

  • Передавати результат виклику замість самої функції: setTimeout(sayHi(), 100) виконає sayHi негайно і передасть таймеру undefined. Правильно: setTimeout(sayHi, 100).
  • Очікувати, що значення, отримане в асинхронному колбеку, буде доступне одразу після виклику функції: воно з'явиться лише тоді, коли колбек справді виконається.
  • Ігнорувати перший аргумент з помилкою в error-first колбеках і продовжувати роботу з порожніми даними.
  • Викликати callback двічі (і на успіху, і на помилці), бо забули return після обробки збою.
  • Втрачати this, передаючи метод об'єкта як callback: element.addEventListener('click', obj.handler) викличе метод з іншим this. Рятує стрілкова функція або bind.
  • Будувати глибокі вкладені колбеки там, де давно можна взяти Promise чи async/await.

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

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

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