Skip to main content

Що таке "callback hell"?

Callback hell - це ситуація, коли в коді використовується багато вкладених callback-функцій, через що код стає:

  • важким для читання,
  • складним для підтримки,
  • і практично неможливим для налагодження.

Простіше кажучи:

"Callback hell" - це пекло для розробника, коли колбеки викликають інші колбеки, утворюючи «піраміду жаху».


Приклад «пекла з колбеків»

javascript
getUser('tim', (user) => { getPosts(user.id, (posts) => { getComments(posts[0].id, (comments) => { saveToDB(comments, (result) => { console.log('Готово!'); }); }); }); });

Тут:

  • Кожна функція чекає завершення попередньої,
  • Колбеки вкладені один в одного,
  • Відступи ростуть праворуч,
  • Помилки складно ловити,
  • І код перетворюється на «ліс» із дужок.

Це і є callback hell.


Чому це погано

ПроблемаОпис
Погана читабельністьВкладені рівні роблять код візуально важким
Складно обробляти помилкиКожному колбеку потрібна своя перевірка if (err) ...
Залежності ланцюжкомКожна операція чекає попередню
Складне налагодженняВинятки губляться, stack trace довгий
Неможливо повторно використати кодВсе "зашите" у вкладені анонімні функції

Звідки він береться

Проблема характерна для асинхронного JavaScript часів до ES6, коли все будувалося на колбеках:

  • Робота з API: XMLHttpRequest
  • Таймери: setTimeout
  • Події: addEventListener
  • Node.js до Promise API

Як виглядав старий асинхронний код

javascript
fs.readFile('user.json', (err, data) => { if (err) throw err; getPosts(JSON.parse(data).id, (err, posts) => { if (err) throw err; sendEmail(posts, (err) => { if (err) throw err; console.log('Email надіслано!'); }); }); });

Все працює, але виглядає жахливо

  • складно підтримувати, тестувати і розширювати.

Як уникнути callback hell

1. Використовувати іменовані функції

Це хоча б "випрямляє" код:

javascript
function handleUser(user) { getPosts(user.id, handlePosts); } function handlePosts(posts) { getComments(posts[0].id, handleComments); } function handleComments(comments) { console.log('Готово!'); } getUser('tim', handleUser);

2. Використовувати Promise

javascript
getUser('tim') .then(getPosts) .then(getComments) .then(saveToDB) .then(() => console.log('Готово!')) .catch(console.error);

Код став лінійним, читається зверху вниз, без вкладеності.


3. Використовувати async / await

javascript
async function run() { try { const user = await getUser('tim'); const posts = await getPosts(user.id); const comments = await getComments(posts[0].id); await saveToDB(comments); console.log('Готово!'); } catch (err) { console.error(err); } } run();

Тепер код виглядає як синхронний, але працює асинхронно. Жодних «пірамід жаху».


Проста аналогія

Callback hell - це як готувати вечерю, де кожен крок вимагає дзвінка другу, а той дзвонить ще комусь, і всі чекають один одного, поки не вийде «піраміда дзвінків».


ПІДСУМОК

ТермінОпис
Callback hellСитуація з глибокою вкладеністю callback-функцій
ПричинаАсинхронний код, побудований на колбеках
НедолікиПогана читабельність, складне налагодження, дублювання логіки
РішенняPromise, async/await, іменовані функції, декомпозиція

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

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

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