JavaScript | Promise rejected | Отклонённое Обещание

JavaScript | Promise rejected | Отклонённое Обещание

Работая с JavaScript или NodeJS важно понимать как себя ведут «Отклонённые Обещания». Я говорю о таких объектах Promise, где в переданной функции «Исполнитель» была вызвана функция отклонения — reject(). То есть когда ветвление алгоритма привело нас к reject(), а не к resolve().

Называть функцию «Исполнитель» мы можем как угодно (здесь мы используем стрелочную безымянную функцию). Имена функций «разрешения» или «отклонения» могут тоже называться как угодно. Мы стараемся придерживаться адекватного именования:

  1. resolve — разрешает
  2. reject — отклоняет
Функция Исполнитель с Разрешителем и Отклонителем в объекте Обещания - Promise JavaScript
Функция Исполнитель с Разрешителем и Отклонителем в объекте Обещания — Promise JavaScript

Подробно ознакомиться с объектами Обещаний можно в стандарте ECMAScript — https://tc39.es/ecma262/#sec-promise-objects.

 

Видеоролик на тему отклонённых обещаний

async await только с отклонённым обещанием без метода catch() и без метода then()

Давайте смотреть на самый простой пример № 1. Здесь мы используем только одно Обещание!

let pr1 = new Promise((resolve, reject)=>{reject('qwe')});
async function f1(pr){
   return await pr
};
let t1 = await f1(pr1);

Что мы пытаемся сделать? Мы создаём такое Обещание «pr1«, которое 100% отклоняется в момент вызова. В функцию отклонения мы передаём строку ‘qwe‘, чтобы в будущем с ней как-то взаимодействовать. Мы хотим, чтобы так было.

Затем мы объявляем асинхронную функцию «f1«, которая будет ждать выполнения нашего Обещания. И далее мы хотим присвоить результат выполнения Обещания в переменную «t1» — условно text1.

Что у нас получится при вызове этого алгоритма?

Отклонённое обещание не присвоило значение через await - JavaScript
Отклонённое обещание не присвоило значение через await — JavaScript

Весь наш алгоритм завершился внезапно из-за ошибки — «Uncaught qwe«. Из-за этого, переменная «t1» вообще не создалась так как программа вышла из выполнения на уровне вызова асинхронной функции. Вызов await начался, но из-за ошибки внезапно завершился и объявление с присваиванием не выполнилось.

Какой вывод можно сделать из этой ситуации? Чистое Обещание без дополнительных методов обработки опасно для работы приложения. Асинхронная функция, ожидающая результат работы Обещания, не будет выполнена. async await в этом случае не сработает как должен.

 

async await с отклонённым обещанием и с методом catch(), но без метода then()

Как правильно работать с отклонённым обещанием? Нам нужно научиться перехватывать ошибки у отклонённых обещаний. Главное, что в стандартном наборе среды выполнения JavaScript кода, этот функционал есть. Я говорю о методе catch(). Его основное отличие от метода then() в том, что он умеет обрабатывать только отклонённые обещания!

Метод catch() принимает только функцию отклонения основного обещания - JavaScript
Метод catch() принимает только функцию отклонения основного обещания — JavaScript

Что важно знать? Метод catch() вызывается на каком-то объекте Обещания и всегда возвращает нам новый объект Обещания. Посмотрим на пример № 2.

let pr2 = new Promise((resolve, reject)=>{reject('qwe')});
  pr2.constructor.name; // 'Promise'
let c2 = pr2.catch();
  c2.constructor.name;  // 'Promise'

Результат последовательного выполнения данных команд:

Метод catch() возвращает новое Обещание - JavaScript
Метод catch() возвращает новое Обещание — JavaScript

Можем выполнить сравнение объектов и убедиться, что это разные объекты:

pr2 == c2
false

pr2 === c2
false
Обещание не равно новому Обещанию из метода catch() - JavaScript
Обещание не равно новому Обещанию из метода catch() — JavaScript

 

Обратите внимание, в метод catch() мы не передали никакую функцию, которая бы обрабатывала результат отклонения нашего основного Обещания. То есть мы никак не работаем с отклонением. Давайте это исправим в примере № 3.

let pr3 = new Promise((resolve, reject)=>{reject('qwe')});
let c3 = pr3.catch(o => 'Перехватили результат отклонения: ' + o);
let t3 = await c3;

Что получим на этот раз? Я работаю в консоли браузера на самом верхнем уровне видимости.

Успешно перехватили результат отклонённого Обещания и преобразовали его во втором Обещании - JavaScript
Успешно перехватили результат отклонённого Обещания и преобразовали его во втором Обещании — JavaScript

Обратите внимание на этот пример! Он наиболее показательный:

Перехват отклонённого обещания со статусом rejected вернул новое обещание со статусом fulfilled - JavaScript
Перехват отклонённого обещания со статусом rejected вернул новое обещание со статусом fulfilled — JavaScript

Перехват отклонённого обещания со статусом «rejected» вернул новое обещание со статусом «fulfilled«. То есть метод catch() не только создал второе Обещание, но он также смог изменить статус у этого Обещания на «выполненное — разрешённое». Вместо ошибки мы получили полезную информацию. Программа не сломалась.

В этот раз мы использовали передачу параметра в метод catch() — это стрелочная функция «(o) => ‘Перехватили результат отклонения: ‘ + o«.

 

async await с отклонённым обещанием и с методом then(), но без метода catch()

С отклонённым обещанием можно также работать и через метод then(). С ним мы тоже сможем перехватывать ошибки у отклонённых Обещаний. Его основное отличие от метода catch() в том, что он умеет одновременно обрабатывать разрешённые и отклонённые обещания! Он умеет принимать функцию обработки разрешения и функцию обработки отклонения.

Метод then() принимает функцию отклонения основного обещания и функцию разрешения - JavaScript
Метод then() принимает функцию отклонения основного обещания и функцию разрешения — JavaScript

Обязательно различайте смысл работы 4 функций:

  1. resolve — разрешает Обещание (делает Обещание разрешённым)
  2. reject — отклоняет Обещание (делает Обещание отклонённым)
  3. onFulfilled — обрабатывает результат разрешённого Обещания
  4. onRejected — обрабатывает результат отклонённого Обещания

То есть две функции влияют на присвоение статуса Обещания, а две другие знают что в этом случае нужно делать (при том или ином статусе Обещания). Генераторы и Обработчики.

Пример № 5

let pr5 = new Promise((resolve, reject)=>{reject('qwe')});
let c5 = pr5.then(undefined, o=>'Перехватили результат отклонения в then(): ' + o);
let t5 = await c5;

Мы передаём в метод then() только обработчик отклонённого Обещания. Для разрешённого Обещания у нас нет функции-обработчика — undefined.

Перехват отклонённого обещания методом then() - JavaScript
Перехват отклонённого обещания методом then() — JavaScript

 

Как писать код при работе с отклонёнными обещаниями в JavaScript?

Общая рекомендация такая. Результат разрешения и результат отклонения должны иметь одинаковую структуру данных. Если вы ждёте от обещания строку, то в случае отклонения тоже должна перехватываться строка. Если вы ждёте от обещания объект, то в случае отклонения тоже должен перехватываться объект. И так далее.

Функция «Исполнитель» в желаемом Обещании может иметь сильное ветвление логики работы. Не забывайте везде разрешать или отклонять Обещание в каждом из блоков ветвления. Если вы пропустите какой-то блок ветвления алгоритма, то вы можете получить Обещание, которое ожидает выполнения — это Обещание со статусом «pending«. Это очень опасно при сборке динамических данных из разных баз данных. Если какой-то фрагмент данных не приедет, то всё приложение может повиснуть.