Обещание - это объект, представляющий окончательное завершение или сбой асинхронной операции.
Это то, что MDN определяет Promise для JS. Обещание может иметь одно из трех состояний. Это может быть Решено, Отклонено или Ожидает.
Давайте посмотрим на объект Promise и его методы-прототип, чтобы лучше понять, как он работает.
У объекта Promise есть пять статических методов и три метода-прототипа, как показано на диаграмме объектов ниже.
Он также имеет два внутренних свойства [[PromiseStatus]] и [[PromiseValue]], которые недоступны напрямую, но играют очень важную роль, удерживая статус и результат (значение) экземпляра обещания.
Прежде чем мы углубимся в объект обещания и его методы. Позвольте мне рассказать вам историю, которую вы сможете использовать позже, чтобы понять, как работает обещание. А пока я могу сказать вам, что Promise работает так же, как вы даете кому-то обещание.
Этакая история обещания:
Однажды в пятницу, выходя из офиса, я позвонил жене и попросил ее приготовить мое любимое блюдо. Она ответила: да, обещаю (обещание1), я подам ваше любимое блюдо на ужин сегодня, когда вы вернетесь домой. Она также упомянула: «Мне нужно пойти в супермаркет, чтобы купить ингредиенты, прежде чем я начну».
[Обещание1 «приготовить блюдо» выполнено] [статус обещания - ожидает выполнения]
Возвращаясь домой, я подумал о том, чтобы позвонить своему другу на ужин. Я позвонил своему другу и попросил его прийти домой с несколькими банками пива. Он пообещал и сказал, что будет там к ужину.
[Promise2 «возвращайся домой» выполнено] [статус обещания ожидает выполнения]
Что ж, когда я добрался до дома, мой друг уже пришел [Promise2 resolved], но моя жена не смогла приготовить мое любимое блюдо, так как не смогла найти все ингредиенты (вместо этого она приготовила что-то другое) [Promise1 отклонено]
История продолжается ..
В приведенной выше истории сортировки у нас есть два обещания: makeDish и comeHome. Давайте создадим эти обещания с помощью объекта JavaScript и разберемся, как он работает.
Синтаксис конструктора обещания:
let promise = new Promise((resolve, reject) => { // executor (executor code goes here) });
Функция исполнителя:
(resolve, reject) = ›{// тело функции-исполнителя}
Функция исполнителя принимает еще два параметра: разрешение и отклонение. resolve и reject - это методы экземпляра объекта Promise, поэтому вам не нужно писать эти методы (см. диаграмму прототипа выше)
Тело функции-исполнителя - это то место, где вы можете выполнять асинхронный вызов, такой как вызов XHR или вызов API выборки. И в зависимости от статуса вызова XHR / fetch вы можете разрешить или отклонить обещание. Скоро мы рассмотрим асинхронный вызов.
let makeDish = new Promise((resolve, reject)=>{ let allIngredientFound = false;// this can be a result of an asynchronous call. if(allIngredientFound){ resolve(console.log(“favoruit dish cooked”)); }else{ reject(console.log(“Dish not cooked as all ingredient not found”)); } }); //result - Dish not cooked as all ingredient not found
* вы получите ошибку uncaught в обещании, поскольку отклонение не обрабатывается с помощью catch ();
Скоро мы увидим, как поймать отклоненное обещание.
let comeHome = new Promise((resolve, reject)=>{ let buyDrink = true; if(buyDrink){ resolve(console.log("came home with drink")) }else{ reject(console.log("could not come home as drink not found")) } }); // result - came home with drink
Мы создали обещание «makeDish», создав экземпляр конструктора Promise с помощью оператора new (да, Promise - это шаблон конструктора). Конструктор Promise принимает в качестве параметра функцию обратного вызова (часто называемую функцией-исполнителем).
В нашем примере «makeDish» у нас нет асинхронного вызова, но у нас есть переменная «allIngredientFound», которая содержит значение «false». Давайте рассмотрим, что «allIngredientFound» является результатом асинхронного метода, который возвращает «false».
Затем на основе значения allIngredientFound мы разрешаем или отклоняем обещание, предоставляя «данные результата» или «причину отказа».
if(allIngredientFound){ resolve("favourit dish cooked"); }else{ reject("Dish not cooked as all ingredient not found"); }
«Данные результата» или «причина отклонения» могут быть любыми данными, такими как «любимое приготовленное блюдо», или это могут быть необработанные / обработанные данные, полученные в результате асинхронного вызова.
.then ()
Что ж, в приведенном выше примере создаются два обещания (makeDish и comeHome), но как получить доступ к этому обещанию. Я имею в виду, если кто-то пообещает вам, как вы узнаете результат (статус обещания).
Чтобы узнать статус обещания, нужно подписаться на обещание, используя метод прототипа Promise «then ()».
Then () принимает в качестве параметра два обратных вызова. Первый параметр обратного вызова выполняется, когда обещание разрешено, а второй обратный вызов (необязательно) выполняется, если обещание отклоняется.
let comeHome = new Promise((resolve, reject)=>{ let buyDrink = false; if(buyDrink){ resolve("came home with drink") }else{ reject("could not come home as drink not found") } }); comeHome.then(result => console.log(result), error => console.log(error)); //Result : could not come home as drink is not found
Здесь обещание отклоняется как buyDrink = false; и отклоненное обещание перехватывается с помощью then ();
Примечание: первый обратный вызов в then () не вызывается, поскольку обещание отклоняется. Попробуйте сделать «buyDrink = true» в приведенном выше коде, и вы увидите, что результатом будет «пришел домой с напитком» и будет вызван первый обратный вызов внутри then ().
.catch ()
Как уже упоминалось, второй обратный вызов не является обязательным. Обычно, чтобы получить статус отклоненного обещания, мы используем другой метод прототипа catch ().
Метод Catch принимает в качестве аргумента обратный вызов
comeHome.catch(error=>console.log(error)); //This interns internally calls comeHome.then(undefine, error=>console.log(error));
.finally ()
Метод finally будет выполняться, даже если обещание отклонено или разрешено. Это полезно, если нужно выполнить некоторую обработку после того, как обещание разрешено или отклонено.
comeHome.then(result=>console.log(result)).catch(error=>console.log(error).finally(()=>console.log(“finally do something”)));
Статические методы
Promise имеет пять статических методов, упомянутых в начале. Мы уже видели, как и где использовать обещания.resolve () и обещание.reject (). Давайте по очереди рассмотрим остальные три метода.
.all ()
Предположим, что вам нужно выполнить несколько асинхронных вызовов одновременно, используя обещание, чтобы получить разные данные JSON с сервера и агрегировать результат.
Для этого вы можете использовать обещание.all (), передав массив обещаний в качестве параметра, который вернет вам одно обещание.
Promise.all([ new Promise(resolve => setTimeout(() => {resolve(3)}, 1000)), new Promise(resolve => setTimeout(() => {resolve(1)}, 3000)), new Promise(resolve => setTimeout(() => {resolve(2)}, 2000)), ]).then(res=>console.log(res)); Result — [3,2,1] *Note that the result is an array object.
Давайте посмотрим на реальный пример вызова сервера.
let urls = [ "https://swapi.co/api/people/1/", "https://swapi.co/api/planets/3/", "https://swapi.co/api/starships/9/" ]; let requests = urls.map(url => fetch(url)); Promise.all(requests).then(responses => responses.forEach( response => console.info(response) ));
Ответом будет объект массива, содержащий данные JSON из URL-адресов.
Если какое-либо обещание не выполняется, обещание, возвращаемое Promise.all (), немедленно отклоняется и возвращает ошибку, указанную в reject ().
Promise.all([ new Promise(resolve => setTimeout(() => {resolve(3)}, 1000)), new Promise(resolve => setTimeout(() => {reject(new Error(‘error message))}, 1000)), new Promise(resolve => setTimeout(() => {resolve(2)}, 2000)), ]).then(res=>console.log(res)).catch(err = > console.log(err));
.allSetteled ()
Согласно article: Promise.allSettled дает вам сигнал, когда все входные обещания выполнены, что означает, что они либо выполнены, либо отклонены.
let urls = [ "https://swapi.co/api/people/1/", "https://swapi.co/api/planets/3/", "https://swapi.co/api/starships/9/" ]; let requests = urls.map(url => fetch(url)); Promise.allSetteled(requests).then(responses => responses.forEach( response => console.info(response) )); //try it on your console and see what you get.
.race ()
Как имя функции «race ()», она проходит между обещаниями и возвращает обещание, которое выполняется или отклоняется, как только одно из обещаний выполнено или отклонено.
Promise.race([ new Promise(resolve => setTimeout(() => {resolve(1)}, 4000)), // 1 new Promise(resolve => setTimeout(() => {resolve(2)}, 3000)), // 2 new Promise(reject=> setTimeout(() => {reject(3)}, 2000)), // 3 ]).then(res=>console.log(res), err=>console.log(err)); Result : 3 // promise 3 gets rejected first .
Это все о Promise в JavaScript. Надеюсь, я рассмотрел все аспекты обещания. Чтобы узнать больше о его характеристиках и примерах, я рекомендую Сеть разработчиков Mozilla (MDN).