Promise в JavaScript
Прежде чем изучать эту главу, вам нужно понять, что такое асинхронное программирование. Вы можете обратиться к:Асинхронное программирование на JavaScript
Promise — это класс, предоставленный ECMAScript 6 и предназначенный для более элегантного написания сложных асинхронных задач.
Promise — это объект в JavaScript, используемый для обработки асинхронных операций. Он представляет собой конечное завершение (или неудачу) асинхронной операции и её результирующее значение.
Проще говоря, Promise — это «обязательство», что результат будет возвращён в какой-то момент в будущем (либо успешный результат, либо причина неудачи).
Promise имеет три состояния:
- pending: начальное состояние, ни fulfilled, ни rejected
- fulfilled: означает, что операция успешно завершена
- rejected: означает, что операция завершилась неудачей

Пример
// код асинхронной операции
if (/* Операция выполнена успешно */) {
resolve('Успешный результат'); // Изменить состояние Promise на fulfilled
} else {
reject('Причина ошибки'); // Изменить состояние Promise на rejected
}
});
Поддержка браузеров
Поскольку Promise появился в ES6, некоторые старые браузеры его не поддерживают. Apple Safari 10 и Windows Edge 14 и выше начали поддерживать возможности ES6.
Ниже показана поддержка Promise в браузерах:
| Chrome 58 | Edge 14 | Firefox 54 | Safari 10 | Opera 55 |
Как использовать Promise
Метод then()
then()Этот метод используется для указания функций обратного вызова, когда состояние Promise изменяется на fulfilled или rejected.
Пример
(result) => {
// Обработка случая успеха
console.log('Success:', result);
},
(error) => {
// Обработка случая ошибки
console.error('Failure:', error);
}
);
Метод catch()
catch()Этот метод специально используется для обработки случаев, когда Promise находится в состоянии rejected.
Пример
.then((result) => {
console.log('Success:', result);
})
.catch((error) => {
console.error('Failure:', error);
});
Метод finally()
finally()Этот метод выполняется независимо от конечного состояния Promise.
Пример
.then((result) => {
console.log('Success:', result);
})
.catch((error) => {
console.error('Failure:', error);
})
.finally(() => {
console.log('Операция завершена');
});
Цепочка Promise
Мощная особенность Promise заключается в том, что можно объединять в цепочку несколько асинхронных операций.
Пример
.then((result) => doSecondThing(result))
.then((newResult) => doThirdThing(newResult))
.then((finalResult) => {
console.log('Итоговый результат:', finalResult);
})
.catch((error) => {
console.error('Ошибка где-то в цепочке:', error);
});
Статические методы Promise
Promise.all()
Ожидает завершения всех Promise или отказа любого одного Promise.
Пример
.then((results) => {
// results — это массив, содержащий все результаты Promise
console.log(results);
})
.catch((error) => {
// Сюда попадаем, если любой Promise завершится с ошибкой
console.error(error);
});
Promise.race()
Возвращает результат первого Promise, который завершится (независимо от успеха или неудачи).
Пример
.then((result) => {
// Используйте результат первого завершившегося Promise
console.log(result);
})
.catch((error) => {
// Если первый завершившийся Promise отклонён
console.error(error);
});
Promise.resolve() и Promise.reject()
Быстро создать выполненный или отклонённый Promise.
Пример
const rejectedPromise = Promise.reject('Immediately rejected reason');
Примеры практического применения
Пример 1: Использование Promise для обработки AJAX-запросов
Пример
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.onload = () => {
if (xhr.status === 200) {
resolve(xhr.response);
} else {
reject(new Error(xhr.statusText));
}
};
xhr.onerror = () => reject(new Error('Ошибка сети'));
xhr.send();
});
}
fetchData('https://api.example.com/data')
.then((data) => {
console.log('Данные успешно получены:', data);
})
.catch((error) => {
console.error('Не удалось получить данные:', error);
});
Пример 2: Использование цепочки Promise для обработки нескольких асинхронных операций
Пример
return fetch(`/users/${userId}`);
}
function getPosts(userId) {
return fetch(`/users/${userId}/posts`);
}
getUser(123)
.then((user) => {
console.log('Получение информации о пользователе:', user);
return getPosts(user.id);
})
.then((posts) => {
console.log('Получение постов пользователя:', posts);
})
.catch((error) => {
console.error('Операция не удалась:', error);
});
Типичные проблемы и лучшие практики
1. Избегайте вложенности промисов
Неправильный подход (Promise-версия ада колбэков):
Пример
doSecondThing(firstResult).then((secondResult) => {
doThirdThing(secondResult).then((thirdResult) => {
console.log(thirdResult);
});
});
});
Правильный подход (использовать цепочки):
Пример
.then(doSecondThing)
.then(doThirdThing)
.then((finalResult) => {
console.log(finalResult);
});
2. Всегда обрабатывайте отклонения
Если не обрабатывать отклонения Promise, это приведёт к ошибкам "uncaught promise rejection". Всегда используйте.catch()илиtry/catch(в async/await) для обработки ошибок.
3. Промисы не поддерживают отмену
После создания Promise нельзя отменить. Если вам нужна возможность отмены, рассмотрите использование AbortController или других паттернов.
4. Часто задаваемые вопросы
Вопрос: Можно ли изменить порядок then, catch и finally на обратный?
Ответ: Да, эффект абсолютно тот же. Но это не рекомендуется; лучше писать программу в порядке then-catch-finally.
Вопрос: Кроме блока then, можно ли использовать два других блока несколько раз?
Ответ: Да. finally, как и then, выполняется по порядку, но выполнится только первый блок catch, если только в блоке catch не возникнет исключение. Поэтому лучше располагать только один блок catch и один блок finally.
Вопрос: Как прервать блок then?
Ответ: Блок then по умолчанию выполняется последовательно сверху вниз; return не может его прервать. Можно использовать throw, чтобы перейти к catch и тем самым прервать выполнение.
Вопрос: Когда целесообразно использовать Promise вместо традиционных функций обратного вызова?
Ответ: Когда несколько асинхронных операций должны выполняться последовательно. Например, если вы хотите последовательно проверить имя пользователя и пароль через асинхронные методы, нужно сначала асинхронно проверить имя пользователя, затем асинхронно проверить пароль — это ситуация, хорошо подходящая для Promise.
Вопрос: Promise — это метод преобразования асинхронного в синхронное?
Ответ: Вовсе нет. Promise — это просто более удобный стиль программирования.
Вопрос: Когда нужно написать ещё один then вместо продолжения программирования в текущем then?
Ответ: Когда нужно вызвать другую асинхронную задачу.
Promise и async/await
В ES2017 появились async/await, которые основаны на Promise и предоставляют более интуитивный синтаксис для обработки асинхронных операций.
Пример
try {
const user = await getUser(123);
const posts = await getPosts(user.id);
console.log('User posts:', posts);
} catch (error) {
console.error('Не удалось получить данные:', error);
}
}
Хотя async/await более читаем, понимание основных концепций Promise всё ещё очень важно, потому что это основа async/await.
Другие расширения