Природжена асинхронна природа JavaScript дозволяє нам виконувати неблокуючі операції, такі як мережеві запити, файл I/O та інші. Однак керувати цими операціями може бути складно. Цей блог проведе вас через еволюцію обробки асинхронних операцій у JavaScript, починаючи з колбеків (callbacks) і переходячи до promises та async/await.
Проблема з колбеками: Callback Hell
Колбеки були початковим способом обробки асинхронних операцій у JavaScript. Колбек — це функція, яка передається як аргумент іншій функції, яка буде виконана пізніше. Хоча це було зрозуміло для простих завдань, колбеки можуть стати громіздкими, коли вони вкладені.
Приклад Callback Hell
getUser(userId, (user) => {
getPosts(user.id, (posts) => {
getComments(posts[0].id, (comments) => {
console.log(comments);
});
});
});
Чому це проблема:
- Вкладена структура: Важко читати та підтримувати.
- Обробка помилок: Складно передавати та керувати помилками.
Від Callback Hell до Promises
Promises були введені для вирішення цих проблем. Promise представляє значення, яке може бути доступне зараз, в майбутньому, або ніколи.
Приклад використання Promises
getUser(userId)
.then((user) => getPosts(user.id))
.then((posts) => getComments(posts[0].id))
.then((comments) => console.log(comments))
.catch((error) => console.error(error));
Переваги:
- Спрощена структура: Легше читати та налагоджувати.
- Ланцюгування: Дозволяє чистіший потік асинхронних операцій.
- Обробка помилок:
catch()
обробляє помилки на будь-якому етапі.
Створення Promises
Створення власних promises дозволяє інкапсулювати асинхронну логіку.
Приклад кастомного Promise
const fetchData = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
const data = "Fetched data!";
resolve(data);
}, 1000);
});
};
fetchData()
.then((data) => console.log(data))
.catch((error) => console.error(error));
Використання Promises з Async/Await
Async/await, введений у ES2017, побудований на promises і надає синтаксис, подібний до синхронного, для обробки асинхронного коду.
Приклад Async/Await
const fetchComments = async (userId) => {
try {
const user = await getUser(userId);
const posts = await getPosts(user.id);
const comments = await getComments(posts[0].id);
console.log(comments);
} catch (error) {
console.error(error);
}
};
fetchComments(1);
Чому використовувати async/await?
- Читабельність: Код лінійний і простий для розуміння.
- Обробка помилок: Використовуйте
try...catch
для чистішого управління помилками.
Повернення значень з Async функцій
Async функції завжди повертають promise. Щоб отримати повернуте значення, потрібно використовувати await
або .then()
.
Приклад
const addNumbers = async (a, b) => a + b;
addNumbers(3, 7).then((result) => console.log(result)); // Виведеться: 10
Очікування кількох Promises одночасно
Ви можете використовувати Promise.all
або Promise.allSettled
для одночасного виконання кількох promises.
Приклад Promise.all
const fetchAllData = async () => {
const userPromise = getUser(1);
const postsPromise = getPosts(1);
const [user, posts] = await Promise.all([userPromise, postsPromise]);
console.log(user, posts);
};
fetchAllData();
Чому використовувати?
- Покращує продуктивність, запускаючи promises паралельно.
- Спрощує обробку кількох асинхронних операцій.
Висновок
JavaScript зробив великий крок від колбеків до async/await. Кожен етап зробив асинхронне програмування більш керованим та менш схильним до помилок. Зрозумівши та використовуючи promises і async/await, ви можете писати більш чистий, ефективний та легший для підтримки код.
Який підхід ви надаєте перевагу і чому? Поділіться своїми думками в коментарях!
Перекладено з: Understanding Asynchronous JavaScript: From Callback Hell to Async/Await