Промисы (Promises)
Promise (обещание) — часть стандарта es6 и специальная функция (typeof Promise === 'function'
),
которую можно использовать для отложенных и асинхронных операций.
Многие современные стандарты и API имеют promise интерфейсы.
Синтаксис
const promise = new Promise(function(resolve, reject) { });
promise.then(result => { }, error => { });
// или
promise.then(result => {}).catch(error => {});
Описание
Для начала, давайте определимся что Promise может находиться в трёх состояниях:
- ожидание (pending) — начальное состояние, не выполнено и не отклонено;
- выполнено (fulfilled, resolved) — операция завершена успешно;
- отклонено (rejected) — операция завершена с ошибкой.
Итак, при создании промис находится в ожидании (pending), затем он может стать выполненным (resolved) или отклонённым (rejected). В первом случае он может вернуть результат операции, а во втором ошибку (причину отказа). В любом из этих случаев вызовется прикреплённый обработчик then.
Примечания:
- Промис может выполниться только один раз;
- Промис не может поменят свой статус выполнения;
- Промис нельзя отменить;
- Выполнение обработчика then помещается в конец очереди main thread, примерно как
setTimeout(..,0)
; - Обработчик then выполнится в любом случае, независимо от того был он прикреплён до или после того, как выполнился промис (resolved или rejected).
Цепочки
Обработчик then (как и catch) возвращает промис, соответственно мы можем организовать цепочку операций (синхронных или асинхронных), например для последовательной обработки данных.
promise.then(result1 => {
// возвращаем изменённые данные
}).then(result2 => {
// или новый промис
});
Если возвращать значение, оно автоматически завернётся в resolved промис, и следующий then будет вызван с этим значением. Если же возвращать промис, то следующий then дождётся выполнения этого промиса, и только потом будет вызван.
Последовательное выполнение
Следующий пример показывает как можно использовать динамические цепочки в циклах:
chapterUrls.reduce((sequence, chapterUrl) => {
return sequence.then(function() {
return getJSON(chapterUrl);
}).then(function(chapter) {
addHtmlToPage(chapter.html);
});
}, Promise.resolve());
Promise.resolve()
— выполненный промис, и начало нашей цепочки.
Параллельное выполнение
Некоторые операции браузер может делать параллельно, например загрузку ресурсов. С помощью промисов мы можем легко выполнить некоторый код после загрузки всех необходимых ресурсов:
// arrayOfPromises = массив промисов.
// один промис = загрузка одного ресурса.
Promise.all(arrayOfPromises)
.then(function(arrayOfResults) {
// arrayOfResults = массив возвращенных промисами значений
// в том же порядке, в котором вы их передали.
});