HTML / CSSJavaScriptNode jsПаттерны проектированияПрактические

Promises

Теоретически JS promises ничем не отличаются от обещаний в реальной жизни - в сочетании с условиями, ориентированными на результат.

js promises in real life

Promise - это действие, которое гарантирует результат в будущем, результат может быть ожидаемым (положительным), а если что-то пойдет не так, результатом будет то, чего не ожидали (отрицательный). Итак, выполняя обещание, мы также приближаемся к условиям - что, если:

  • Если Майк сможет убрать свою комнату (выполнение условия обещания): Он уходит в футбол !!
  • Но что, если он не убирает (не выполняет условие обещания): ему нужно постирать.
ℹ️Примечания

С точки зрения программирования это поведение представляет собой асинхронное поведение. Ожидается, что Майк будет убирать свою комнату синхронно, но условные действия (обратные вызовы) будут выполняться в будущем (независимо от того, идет ли он играть или стирает). Предположим, «Уборка комнаты» - это действие, которое приводит к успеху или неудаче. Затем «Футбол» и «Прачечная» - это асинхронные обработчики, ожидающие завершения действия «Уборка комнаты». И в зависимости от того, будет ли результат успешным или неудачным, обработчики «Футбол» или «Прачечная» будут выполнены АСИНХРОННО

1const roomCleaning = (resolve, reject)=> {
2 const num = Math.random();
3 if(num >= 0.5){
4 resolve(num);
5 }else{
6 reject(new Error(num));
7 }
8 }
9 const promise_1 = new Promise(roomCleaning);
10 promise_1.then((resolveValue)=>{
11 console.log(`Success handler - room claened successfully, football time!!`);
12 console.log(`resolveValue: ${resolveValue}`);
13 });
14
15 promise_1.catch((errorValue)=>{//Failure handler, laundry
16 console.log(`Error handler - room not claened, has to do laundry`);
17 console.log(`error: ${errorValue}`);
18 });
  • roomCleaning - это функция-исполнитель, которая выполняется JS синхронно.
  • Он принимает 2 аргумента: разрешение и отклонение, это просто переменные-заполнители, которые инициализируются самим JS. Resolve представляет собой успех, а отклонение представляет собой сценарий ошибки. Эти обработчики вызываются асинхронно после завершения работы исполнителя.
  • В строке 9 создается новое обещание путем передачи функции исполнителя в качестве аргумента конструктору обещания. Новый экземпляр хранится в переменной promise_1.
  • В строке 10 promise_1.then - это метод, который принимает в качестве аргумента метод обработчика успеха. Какой бы метод ни передавался здесь, он будет вызываться при вызове метода resolve из функции-исполнителя.
1promise_1.then((resolveValue)=>{
2console.log(`Success handler - room cleaned successfully, football time!!`);
3console.log(`resolveValue: ${resolveValue}`);
4});

Переданный callback также принимает аргумент (resolveValue), когда вы вызываете resolve(num) в функции исполнителя, значение num передается как значение аргумента resolveValue.

  • Точно так же callback, предоставленный функции catch, выполняется JS при вызове reject(error).

В идеале значение, переданное в reject, должно представлять объект ошибки, чтобы JS передавал этот объект обработчику catch - там разработчики должны обрабатывать ошибку.

К одному обещанию можно привязать несколько обработчиков К одному и тому же promise можно присоединить несколько обработчиков, это приводит к регистрации нескольких асинхронных обратных вызовов для выполнения при вызове метода resolve / reject. Все обратные вызовы будут выполнены в том же порядке, что и зарегистрированные:

1const roomCleaning = (resolve, reject)=> {
2 let num = Math.random();
3 if(num >= .5){
4 resolve(num);
5 }else{
6 reject(new Error(`oops!!, the number is ${num}`));
7 }
8 }
9 const promise_1 = new Promise(roomCleaning);
10
11 promise_1.then((resolveValue)=>{//First resolve handler
12 console.log(`Resolve handler 1, value is ${resolveValue}`);
13 console.log(`room claened successfully, football time!!`);
14 });
15
16 promise_1.then((resolveValue)=>{//Second resolve handler
17 console.log(`Resolve handler 2, value is ${resolveValue}`);
18 console.log(`After footbal, i will go for swimming!!`);
19 });
20
21 promise_1.catch((errorValue)=>{//First reject handler
22 console.log(`Error handler 1, value is ${errorValue}`);
23 console.log(`Room not claened, has to do laundry`);
24 });
25 promise_1.catch((errorValue)=>{//Second reject handler
26 console.log(`Error handler 2, value is ${errorValue}`);
27 console.log(`After laundry, i need to clean my room anyway !!`);
28 });

В приведенном выше примере для обоих зарегистрировано два обработчика - resolve и reject. Оба обработчика выполняются в том же порядке, что и зарегистрированные.

Состояния Promise: Pending, Reject, Resolve

Как мы знаем, Promises - это объекты, предоставляемые JS, которые обертывают и выполняют тяжелую работу по обработке асинхронных вызовов. Но как разработчики узнают текущее состояние обещания, то есть завершен ли исполнитель или вызывается один из обработчиков. Для этого объекта Promise предоставляется свойство состояния. Возможные значения:

  • Pending : Если исполнитель в данный момент в работе.
  • Resolved: Если исполнитель завершает выполнение и вызывается метод resolve.
  • Rejected: Если исполнитель завершил выполнение и вызывается метод reject.

promise states

Когда исполнитель завершает работу, он должен вызвать одну из функций, которые он получает в качестве аргументов:

  • resolve (value) - указать, что задание успешно завершено: устанавливает состояние "fulfilled", устанавливает значение в result.
  • reject (error) - чтобы указать, что произошла ошибка: устанавливает состояние "rejected", устанавливает ошибку в result
ℹ️note
  • Если в исполнителе вызывается resolve. Повторный вызов resolve не повлияет - обработчик then будет вызываться только один раз.
  • Если в исполнителе вызывается reject. Повторный вызов reject не повлияет - обработчик catch будет вызван только один раз.
  • Как только reject / resolve выполнено, обещание считается выполненным. Settled - это не формальное состояние, а имя, присвоенное для определения того, выполнено ли обещание с исполнением исполнителя и обработчика.

Цепочка обещаний

Предположим, вы хотите связать условия. Например: Если Майк может убрать комнату, то он идет на футбол. В футбольном матче, если он забьет гол, то в качестве награды получит возможность посетить музыкальный концерт. Если он не забивает, то чистит свою собаку. Если он не может убрать комнату, он стирает. Если он также не может закончить стирку, он должен вымыть свою собаку. Но если он может постирать белье, то идет на концерт.

Вы можете заметить, что условия порождают больше условных действий. Технически асинхронные обратные вызовы (Football & Laundry) снова создают дополнительные асинхронные обратные вызовы (MusicConcert & DogCleaning). Для этой цели Promises может возвращать новые обещания - это называется цепочкой обещаний(Promise Chain).

promise chain

Итак, по сути: Обещание:

  • Внутренняя функция исполнителя, вы можете вызвать метод resolve для успешного сценария.
  • Вы можете вызвать метод reject для сценариев ошибок.
  • Если выход этой асинхронной функции должен быть входом для другой асинхронной функции, тогда верните новое обещание.

Promise Chaining Workflow

Зачем нужны промисы?

Промисы - это способ организации асинхронного кода. Можно сказать больше: это способ создания асинхронного кода.

❗️Promise

Промисы - это "коробки" для функций. Их особенность в том, что функция внутри промиса исполняется синхронно, но вот результат ее работы возвращается всегда асинхронно.

1const roomCleaning = (resolve, reject) => {
2 const num = Math.random();
3 if (num >= 0.5) {
4 resolve(num);
5 } else {
6 reject(new Error(num));
7 }
8}
9const promise_1 = new Promise(roomCleaning);
10promise_1.then((resolveValue) => {
11 console.log(`Success handler - room cleaned successfully, football time!!. Resolve Value: ${resolveValue}`);
12 // Новое обещание для обработки дополнительных задач, зависящих от этой.
13 // музыкальный концерт vs чистка собак.
14 return new Promise((resolve, reject) => {
15 const number = Math.random();
16 if (number >= 0.5) {
17 resolve(number);
18 } else {
19 reject(number);
20 }
21 })
22
23}).then((resolveValue) => {
24 console.log(`Success_success handler - i am in music concert!!. Resolve Value: ${resolveValue}`);
25}).catch((errorValue) => {
26 console.log(`Error_success handler - no goal, cleaning the dog :( ${errorValue}`);
27})
28
29promise_1.catch((errorValue) => {
30 console.log(`Error handler - room not cleaned, has to do laundry. errorValue: ${errorValue}`);
31
32 // Новое обещание для обработки дополнительных задач, зависящих от этой.
33 // музыкальный концерт vs чистка собак.
34 return new Promise((resolve, reject) => {
35 let number_2 = Math.random();
36 if (number_2 >= 0.5) {
37 resolve(number_2);
38 } else {
39 reject(number_2);
40 }
41 })
42}).then((resolveValue) => {
43 console.log(`Success_error handler - i am in music concert!!. Resolve Value: ${resolveValue}`);
44}).catch((errorValue) => {
45 console.log(`Error_error handler - no goal, cleaning the dog :( ${errorValue}`);
46})

Результат выполнения кода выше можно увидеть в codesandbox:

Original source

Обратите внимание, что отдельные экземпляры promise возвращаются обработчиками reject и resolve родительского обещания. Но по-прежнему печатаются и Error_success, и Error_error, потому что новые обещания, возвращаемые обработчиками, фактически указывают на один и тот же экземпляр обещания.

Promise + setTimeout

1const promise1 = new Promise((resolve, reject) => {
2 setTimeout(function() {
3 resolve('foo');
4 }, 2000)
5})
6promise1.then(val => console.log(val))
7console.log("I promise I'll be first!")

Практика 👩‍💻 👨‍💻

Задание 1.

Написать функцию, которая возвращает Promise. Promise должен вернуть результаты через 2 секунды и вернуть "Hello world"

Задание 2

Написать функцию delay которая получает в качестве параметра время задержки и возвращает promise по завершению ожидания

1function delay(ms){
2
3}
4
5delay(2000).then( (s) => console.log('I am here')) // будет выполнен через 2с

Задание 3

Написать функцию isEvenOrOddAsync, которая принимает параметр data и выполняет следующие

  1. Функция должна возвращать promise
  2. Если данные не являются числами, верните обещание, отклоненное сразу же, и укажите в параметрах reject data should be a numeric type
  3. Если данные являются нечетным числом, верните обещание, разрешенное через 1 секунду, со значением odd
  4. Если данные являются четным числом, верните обещание, отклоненное через 2 секунды, со значением even
Hello