Callback-функція
Callback-функція це функція, яку передають як аргумент в іншу функцію і яка викликається пізніше, коли та завершить свою роботу. Зазвичай колбеки використовують, щоб виконати дію після події, операції або асинхронного процесу, наприклад після завантаження даних, спрацювання таймера чи кліка.
Теорія
TL;DR
- Callback це функція-аргумент, яку викликає не ваш код, а та функція, якій ви її віддали.
- Це можливо тому, що в JavaScript функції є значеннями першого класу: їх можна передавати й повертати.
- Колбеки бувають синхронні (
map,filter,forEach) і асинхронні (setTimeout, обробники подій, запити). - Передавайте посилання на функцію, а не її виклик:
setTimeout(fn, 100), а неsetTimeout(fn(), 100). - Багато вкладених колбеків перетворюються на callback hell, який лікують Promise і
async/await.
Швидкий приклад
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.
const numbers = [1, 2, 3];
// map це функція вищого порядку, стрілкова функція всередині це callback
const doubled = numbers.map(n => n * 2); // [2, 4, 6]Синхронні та асинхронні колбеки
Синхронний callback викликається одразу, у межах того самого виклику функції. Асинхронний потрапляє в чергу і виконається пізніше, коли call stack звільниться.
// синхронний: виконається до наступного рядка
[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 історично склалася домовленість: перший параметр колбека це помилка, а дані йдуть далі. Так викликач може обробити збій у тому самому місці, де отримує результат.
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: його важко читати й ще важче обробляти в ньому помилки.
loadUser(7, (err, user) => {
loadOrders(user.id, (err, orders) => {
loadItems(orders[0].id, (err, items) => {
console.log(items); // вкладеність росте з кожним кроком
});
});
});Сучасне рішення це Promise і async/await: та сама послідовність записується пласко, а помилки ловляться одним try/catch.
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.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.