Пошук уроків, статей та іншого контенту
Об'єкт, що представляє результат асинхронної операції в майбутньому — стани, then/catch/finally та проблема callback hell.
До Promise асинхронні операції, що залежали одна від одної, записувались вкладеними колбеками (модуль «Функції») — кожен наступний крок опинявся на рівень глибше за попередній:
getUser(userId, (user) => {
getOrders(user.id, (orders) => {
getOrderDetails(orders[0].id, (details) => {
console.log(details); // логіка ховається у вкладеності, а не в послідовності дій
});
});
});Цю зростаючу вкладеність жартома називають «callback hell» (пекло колбеків) — код важко читати, ще важче додати обробку помилок для кожного рівня окремо. Promise — стандартний об'єкт, який представляє результат асинхронної операції до того, як цей результат готовий, і дозволяє записувати послідовність кроків без нарощування вкладеності.
pending (очікування) — початковий стан, результат ще не готовий.
fulfilled (виконано) — операція завершилась успішно, є результат.
rejected (відхилено) — операція завершилась помилкою.
Promise може перейти з pending лише один раз, назавжди — у fulfilled або rejected, і назад уже не повертається.
Функція-виконавець (executor) передається в конструктор і отримує дві функції: resolve (викликати при успіху) і reject (викликати при помилці):
function wait(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms); // через ms мілісекунд Promise стає fulfilled
});
}На практиці власні Promise через new Promise(...) пишуть рідко — здебільшого Promise повертають вбудовані API (fetch, таймери, обгортки над файловою системою в Node.js) чи бібліотеки. Розуміння механізму важливе, навіть якщо створювати їх вручну доводиться нечасто.
wait(1000)
.then(() => console.log("Минула секунда"))
.catch((error) => console.error("Щось пішло не так:", error))
.finally(() => console.log("Виконано незалежно від результату"));then(onFulfilled) — виконується, коли Promise переходить у fulfilled; отримує результат як аргумент.
catch(onRejected) — виконується, коли Promise переходить у rejected; отримує причину помилки.
finally(onFinally) — виконується завжди, незалежно від результату; не отримує жодних аргументів, зручний для «прибирання» (наприклад, приховати індикатор завантаження).
function fetchUserData(userId) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (userId <= 0) {
reject(new Error("Некоректний userId"));
return;
}
resolve({ id: userId, name: "Олена" });
}, 500);
});
}
fetchUserData(1)
.then((user) => console.log(user)) // { id: 1, name: "Олена" }
.catch((error) => console.error(error.message));Виклик і resolve, і reject в одній операції — лише перший виклик має ефект, стан Promise уже незмінний після нього, але легко ненавмисно викликати обидва в розгалуженій логіці.
Забування .catch() — необроблена відмова Promise (unhandled rejection) не зупиняє виконання скрипту одразу, як синхронна помилка, але браузер/Node.js попереджають про неї в консолі, і це часто ознака прихованого бага.
Плутанина, що Promise «виконується паралельно» з рештою коду — сам Promise лише представляє майбутній результат; фактична асинхронна робота (таймер, запит) виконується через Web/Node API, як і в моделі Event Loop з попереднього уроку.
Promise — об'єкт, що представляє результат асинхронної операції в одному з трьох станів: pending, fulfilled, rejected. then/catch/finally дозволяють реагувати на результат без нарощування вкладеності колбеків. Наступний урок покаже, як з'єднувати кілька Promise послідовно в ланцюжок.
Спробуйте самостійно
Напишіть функцію rollDice(), що повертає Promise: через 1 секунду він виконується (resolve) з випадковим числом від 1 до 6, а якщо число дорівнює 1 — замість цього відхиляється (reject) з помилкою "Не пощастило".