Callback-функція
Callback-функція, це функція, передана як аргумент іншій функції, щоб виконатися пізніше, після того як станеться якась подія або завершиться операція. Простіше кажучи: «я передаю тобі функцію, виклич її, коли будеш готовий».
Теорія
TL;DR
- Callback, це функція-аргумент, яку приймальна функція викликає в потрібний момент.
- Це можливо, бо функції в JavaScript є об'єктами першого класу і передаються як значення.
- Callback може бути іменованим або анонімним, зокрема стрілковим.
- Callback не обов'язково асинхронний:
array.map(fn)викликає його синхронно,setTimeout(fn), ні. - На колбеках побудовані
setTimeout,addEventListener,fetch().then(). - Глибока вкладеність колбеків називається callback hell, її лікують
Promiseіasync/await.
Швидкий приклад
function sayHello(name) {
console.log(`Hello, ${name}!`);
}
function greet(callback) {
console.log('Getting ready to say hello...');
callback('Oleh');
}
greet(sayHello);
// Output:
// Getting ready to say hello...
// Hello, Oleh!Що відбувається:
sayHello, це callback, переданий уgreet;greetвикликає його всередині себе;- у консоль виводяться два рядки, у тому порядку, в якому виконується код.
Зверніть увагу: у greet передається sayHello без дужок. greet(sayHello()) викликало б функцію одразу і передало б її результат, тобто undefined.
Як це працює
- JavaScript приймає callback як значення, адже функції тут є об'єктами першого класу.
- Приймальна функція не викликає його одразу, а зберігає для подальшого виклику.
- Коли настає потрібний момент (спрацював таймер, завершився запит, сталася подія), рушій викликає цю функцію.
Callback не зобов'язаний бути іменованим, часто передають анонімну функцію:
setTimeout(() => {
console.log('Two seconds have passed');
}, 2000);Тут () => console.log(...), це callback-функція, і вона буде викликана через дві секунди, а не одразу.
Callback у реальних ситуаціях
1. Обробник події. Тут callback викликає браузер, коли користувач клікає:
button.addEventListener('click', () => {
console.log('Button clicked!');
});2. Завантаження даних. Колбеки .then() викликаються після завершення асинхронного запиту:
fetch('/user')
.then(response => response.json())
.then(data => console.log(data));3. Крок алгоритму. Callback не обов'язково пов'язаний з асинхронністю, він може бути просто способом підставити свою логіку:
function calculate(a, b, callback) {
const result = a + b;
callback(result);
}
calculate(2, 3, (sum) => {
console.log('Result:', sum);
});Проблема callback hell
Якщо колбеків забагато, код стає вкладеним і нечитабельним:
getUser(id, user => {
getPosts(user, posts => {
getComments(posts, comments => {
console.log('Done!');
});
});
});Це називають callback hell, «пекло з колбеків»: кожен наступний крок зсуває код праворуч, обробка помилок дублюється на кожному рівні, а послідовність дій стає нечитабельною.
Як позбутися вкладеності
Щоб уникнути пекельних вкладеностей, з'явилися проміси:
getUser(id)
.then(getPosts)
.then(getComments)
.then(console.log);А потім async/await, який повертає асинхронному коду вигляд звичайного послідовного:
const user = await getUser(id);
const posts = await getPosts(user);
const comments = await getComments(posts);
console.log('Done!');Проста аналогія для співбесіди:
Уявіть, що ви замовляєте піцу і кажете: «Коли приготуєте, зателефонуйте мені». Цей дзвінок і є callback.
Підсумок:
| Термін | Опис |
|---|---|
| Callback-функція | Функція, передана в іншу функцію для виконання пізніше |
| Коли викликається | Після події, завершення операції або за умовою |
| Приклади використання | setTimeout, addEventListener, fetch().then() |
| Проблема | Глибока вкладеність (callback hell) |
| Сучасні альтернативи | Promise, async/await |
Типові помилки
- Передавати виклик замість функції.
setTimeout(fn(), 1000)виконуєfnнегайно і передає таймеру її результат. Правильно, цеsetTimeout(fn, 1000). - Вважати всі колбеки асинхронними.
array.forEach(fn)іarray.map(fn)викликають callback синхронно, прямо зараз, і нічого не відкладають. - Втрачати
this. Звичайна функція як callback отримує власнийthis. Використовуйте стрілкову функцію абоbind. - Ігнорувати помилки. У стилі «error-first» перший аргумент, це помилка:
(err, data) => { if (err) ... }. Пропустивши її, ви мовчки втрачаєте збої. - Викликати callback двічі. Якщо після помилки не зробити
return, функція піде далі й викличе callback ще раз, що ламає всю логіку зверху. - Будувати глибокі піраміди. Три й більше рівнів вкладеності, це сигнал переписати код на
Promiseабоasync/await.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.