Использование обещаний в JavaScript позволяет нам писать асинхронный код упорядоченно и предсказуемо. Промисы в JavaScript делают код более понятным для использования другими программистами.
Обещания (Promises) состоят из цепочки вывозов произвольных функций, которые отправляются в метод .then() в качестве аргумента. Причем количество вызовов этого метода не ограниченно.
Метод .then() может принимать два аргумента — fulfilled и rejected. Это функции, которые отвечают за статусы выполнения предыдущего обещания. Функция fulfilled обрабатывает успешный ответ, a rejected обрабатывает исключение, не дожидаясь пока за него возьмется метод .catch().
В свою очередь метод .catch() вызывается в случае исключительной ситуации или ошибки, который работает как обычный обработчик исключений. Он, как правило, вызывается в конце цепочки что бы перехватить и обработать все возможные ошибки.
Как создаются промисы в JavaScript?
Синтаксис объявления обещания в JavaScript выглядит следующим образом:
let promise = new Promise(function(resolve, reject) {
// resolve - успешное выполнение
// reject - ошибка, операция отклонена
})
У конструктора Promise() имеются два параметра — resolve и reject . Они являются функциями, в которые можно передать любые объекты. В случае успешного выполнения операции вызывается функция resolve(). При возникновении ошибки или исключительной ситуации вызывается функция reject(). Условие когда вызывается та или иная функция определяется определяет сам программист.
JavaScript Promise имеет три состояния:
- pending — ожидание ответа. Когда операция еще не выполнена и не завершена с ошибкой (отклонена)
- resolved — операция выполнена и было присвоено значение
- rejected — отклонено или выполнено с ошибкой
В качестве примера можно вывести в консоль обещание, которое мы определили выше:
console.log(promise);
Получим следующее:

В следствии того что наш промис пустой и не выполняет ничего полезного, он не разрешился и находится в статусе pending со значением undefined.
Если внутри промиса будут вызваны методы resolve() или reject(), то он завершится со статусами resolved и rejected соответственно.
Практический пример использования обещаний (promise) в JavaScript
В качестве примера будем перебирать массив объектов с целью получения необходимого нам значения. Массив будет представлять собой список продуктов на первую половину недели:
const dietList = [
{
name: "Понедельник",
products: ["Молоко", "Яйца", "Фрукты"]
},
{
name: "Вторник",
products: ["Печенье", "Творог", "Рис"]
},
{
name: "Среда",
products: ["Масло", "Соль", "Греча"]
}
];
Итак, мы имеем названия дней недели и продукты, которые необходимо купить в определенный день. Конечной целью перебора будет получить список продуктов в обратном порядке, необходимые для покупки в среду.
Для начала создадим JavaScript промис, который обернем в функцию getProduct():
function getProduct(listDays, needDay){
return new Promise(function(resolve, reject){
for (let day of listDays) {
if(day.name == needDay){
return resolve(day.products);
}
}
return reject(new Error("Не найден день в списке"));
});
}
В результате у нас есть функция, с двумя параметрами — список дней недели и искомый день. Внутри определено JavaScript обещание с циклом for..of перебирающий дни. В случае совпадения возвращается resolve() с массивом продуктов, а если день не найден, то отклоняется методом reject() с описанием причины.
После этого вызовем функцию с переданными в нее аргументами:
getProduct(dietList, "Среда")
.then( //#1
function(products){
if(products.length > 0){
return products.reverse();
}else{
throw new Error("Массим со свойствами пришел пустым");
}
}
)
.then( //#2
function fulfilled(poroduct){
console.log(poroduct); // ["Греча", "Соль", "Масло"]
},
function rejected(reason){
console.log(reason); // Error: Массив со свойствами пришел пустым
}
)
.catch(function(err){
console.log(err);
});
В результате того что мы ищем день, который есть в массиве, нам вернется список продуктов, с которым мы можем работать в методе .then() под #2.
Как можно заметить в цепочке #2 в метод передаются два аргумента — это функции fulfilled и rejected. Первая вызовется в случает успешного завершения метода под #1, а вторая отработает в случае если в предыдущих цепочках обещания было вызвано исключение или ошибка. Это завершит обещание, не дав отработать методу catch().
Для наглядности имеет смысл предоставить пример того же кода, но реализованного с помощью коллбек функций. Массив используем тот же:
function getProductCallback(list, needDay, cb){
let searching = false;
for (let day of list) {
if(day.name == needDay){
searching = true;
cb(searching ,day.products, function(products){
if(products.length > 0){
console.log(products.reverse());
}else{
console.error("Массим со свойствами пришел пустым");
}
});
}
}
if(!searching){
cb(searching, 'Не найден день в списке', function(err){
console.error(err);
});
}
}
getProductCallback(dietList, 'Среда', function(err, response, cb){
!err ? cb(response) : cb(response);
});
Таким образом, до появления js промисов подобные задачи решaли с помощью коллбеков. Это подход тоже рабочий, но при необходимости в одной операции вызывать сразу несколько обратных функций со временем код становился менее разборчивым и как следствие тяжело поддерживаемым.
Важно понимать, что JavaScript промисы не призваны заменить callback функции, а лишь дают нам возможность структурировать код при их использовании.