Promise («промис») – это специальный объект в JavaScript, который используется для отложенных и асинхронных вычислений.
Описание на https://developer.mozilla.org
Интерфейс Promise представляет собой обёртку для значения, неизвестного на момент создания промиса. Он позволяет обрабатывать результаты асинхронных операций так, как если бы они были синхронными: вместо конечного результата асинхронного метода возвращается своего рода обещание (дословный перевод слова "промис") получить результат в некоторый момент в будущем.
Синтаксис создания объекта Promise:
1 2 3 |
new Promise(executor); // или new Promise(function(resolve, reject) { ... }); |
где
- executor - функция, которая получает два параметра и выполняется сразу, функции-колбэки resolve и reject предоставляет сам JavaScript:
- resolve - получает один параметр и вызывается, когда асинхронная операция завершилась успешно и вернула результат своего исполнения в виде значения (вызывается при успешном исполнении промиса);
- reject - получает один параметр и вызывается, когда асинхронная операция не удалась, и возвращает значение, указывающее на причину неудачи, чаще всего - объект ошибки (вызывается при неуспешном исполнении промиса).
Т.о. функция executor описывает выполнение какой-то асинхронной работы, по завершении которой необходимо вызвать функцию resolve или reject.
Возвращаемое значение функции executor игнорируется.
Пример:
1 2 3 4 5 6 7 8 9 |
const myFirstPromise = new Promise((resolve, reject) => { setTimeout(() => { // выполняется асинхронная операция, которая в итоге вызовет: // или resolve('успешное завершение'); // resolve(someValue) - успешное завершение // или reject("причина неудачи") // reject(failure reason) - неудача }, 300); }); console.log(myFirstPromise) |
Функция executor должна вызвать что-то одно: или resolve(), или reject() (состояние промиса может быть изменено только один раз), все последующие вызовы resolve() или reject() будут проигнорированы.
В случае, если операция Promise завершена с ошибкой, мы должны вызвать reject(). Это можно сделать с аргументом любого типа (как и resolve()), но рекомендуется использовать объект Error (или унаследованный от него), например:
1 |
reject(Error('Image didn\'t load successfully; error code:' + request.statusText)); |
Promise может находиться в трёх состояниях:
- ожидание (pending): начальное состояние, не исполнен и не отклонён;
- исполнено (fulfilled): операция завершена успешно;
- отклонено (rejected): операция завершена с ошибкой;
- ещё выделяют завершение с любым исходом (settled).
Говорят, что промис находится в состоянии завершён (settled) когда он или исполнен или отклонён, т.е. в любом состоянии, кроме ожидания (это лишь форма речи, не являющаяся настоящим состоянием промиса).
Также можно встретить термин исполнен (resolved) — это значит что промис завершён или "заблокирован" в ожидании завершения другого промиса.
При создании промис находится в ожидании (pending), а затем может стать:
- исполненным (fulfilled), вернув полученный результат (значение), или
- отклонённым (rejected), вернув причину отказа.
В любом из этих случаев вызывается обработчик, прикреплённый к промису методом then(). Если в момент назначения обработчика промис уже исполнен или отклонён, обработчик всё равно будет вызван.
1 2 3 4 5 |
promise.then(function(result) { console.log(result); // обрабатываем результат }, function(err) { console.log(err); // ошибка }); |
Пример использования метода then():
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
const myFirstPromise = new Promise((resolve, reject) => { setTimeout(() => { // выполняется асинхронная операция, которая в итоге вызовет один из колбэков: // или resolve("успешное завершение"); // resolve(someValue) - успешное завершение // или reject("причина неудачи"); // reject(failure reason) - неудача }, 300); }); console.log(myFirstPromise); // Далее используем результат работы resolve с помощью метода then() myFirstPromise.then((resolveArg) => { // resolveArg - это аргумент, переданный в resolve console.log("Хорошо, " + resolveArg +" работы" ); // Хорошо, успешное завершение работы }); |
Так как методы Promise.prototype.then() и Promise.prototype.catch() сами возвращают промис, их можно вызывать цепочкой, создавая соединения.
Свойства объекта Promise:
- Promise.length - значение свойства всегда равно 1 (количество аргументов конструктора);
- Promise.prototype (en-US) - представляет прототип для конструктора Promise.
Методы объекта Promise:
- Promise.then() возвращает Promise и может принимать два аргумента: колбэк-функции (resolve, reject) для случаев выполнения и отклонения промиса. Если один или оба аргумента отсутствуют или их значения не функции, то then пропустит их и не выбросит ошибку. Если для Promise, который переходит в состояние выполнен или отклонён вызван метод then(), и у данного метода нет нужного обработчика, то в таком случае then() просто возвращает промис с состоянием начального Promise, для которого then() был вызван.
-
Promise.catch(function(reason) {//код отказа}) - работает только в случае отклонения (ошибки) промиса и возвращает промис. Используется для отслеживания ошибок с учетом особенностей:
- отслеживается первая из ошибок;
- ошибки, выброшенные из асинхронных функций (например, setTimeout или setInterval) пойманы не будут;
- ошибки, выброшенные после выполнения промиса, будут проигнорированы.
- Promise.all(iterable) - ожидает исполнения всех промисов или отклонения любого из них. Возвращает промис, который исполнится после исполнения всех промисов в iterable. В случае, если любой из промисов будет отклонён, Promise.all будет также отклонён.
- Promise.any() - возвратит единственный объект Promise со значением первого выполненного промиса (метод Promise.any() является противоположностью для Promise.all()). Если ни один из промисов не завершится успешно (если все промисы завершатся с ошибкой, т.е. rejected), тогда возвращённый объект Promise будет отклонён (rejected) с одним из значений: массив содержащий причины ошибки (отклонения), или AggregateError — подкласс Error, который объединяет выброшенные ошибки вместе.
- Promise.allSettled(iterable) - ожидает завершения (как исполнения, так и отклонения) всех полученных промисов. Возвращает промис, который исполняется, когда все полученные промисы завершены (исполнены или отклонены), и содержащий массив результатов исполнения полученных промисов.
- Promise.race(iterable) - ожидает исполнения или отклонения любого из полученных промисов. Возвращает промис, который будет исполнен или отклонён с результатом исполнения первого исполненного или отклонённого промиса из iterable.
- Promise.prototype.finally() - возвращает промис в любом случае его выполнения (вне зависимости успешно или с ошибкой). Это даёт возможность запустить один раз определённый участок кода, который должен выполниться вне зависимости от того, с каким результатом выполнился Promise.
- Promise.reject(reason) - возвращает промис, отклонённый из-за reason.
- Promise.resolve(value) - возвращает промис, исполненный с результатом value.
Использование Promise для перехвата ошибок:
1 2 3 4 5 6 7 8 9 10 11 12 |
let url = "https://jsonplaceholder.typicode.com/posts"; function getPost(url, id) { return Promise.resolve().then(() => { // используем Promise с resolve как обертку id = int(Boolean); // ошибочная функция return fetch(url + `/${id}`).then((response) => { return response.json(); }); }); } getPost(url, 3) .then((post) => console.log(post)) .catch((error) => console.log(error)); // ReferenceError: int is not defined |