Promises
Теоретически JS promises ничем не отличаются от обещаний в реальной жизни - в сочетании с условиями, ориентированными на результат.
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 });1415 promise_1.catch((errorValue)=>{//Failure handler, laundry16 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);1011 promise_1.then((resolveValue)=>{//First resolve handler12 console.log(`Resolve handler 1, value is ${resolveValue}`);13 console.log(`room claened successfully, football time!!`);14 });1516 promise_1.then((resolveValue)=>{//Second resolve handler17 console.log(`Resolve handler 2, value is ${resolveValue}`);18 console.log(`After footbal, i will go for swimming!!`);19 });2021 promise_1.catch((errorValue)=>{//First reject handler22 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 handler26 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
.
Когда исполнитель завершает работу, он должен вызвать одну из функций, которые он получает в качестве аргументов:
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).
Итак, по сути: Обещание:
- Внутренняя функция исполнителя, вы можете вызвать метод
resolve
для успешного сценария. - Вы можете вызвать метод
reject
для сценариев ошибок. - Если выход этой асинхронной функции должен быть входом для другой асинхронной функции, тогда верните новое обещание.
Зачем нужны промисы?
Промисы - это способ организации асинхронного кода. Можно сказать больше: это способ создания асинхронного кода.
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 })2223}).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})2829promise_1.catch((errorValue) => {30 console.log(`Error handler - room not cleaned, has to do laundry. errorValue: ${errorValue}`);3132 // Новое обещание для обработки дополнительных задач, зависящих от этой.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:
Обратите внимание, что отдельные экземпляры 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){23}45delay(2000).then( (s) => console.log('I am here')) // будет выполнен через 2с
Задание 3
Написать функцию isEvenOrOddAsync
, которая принимает параметр data и выполняет следующие
- Функция должна возвращать promise
- Если данные не являются числами, верните обещание, отклоненное сразу же, и укажите в параметрах
reject
data should be a numeric type - Если данные являются нечетным числом, верните обещание, разрешенное через 1 секунду, со значением odd
- Если данные являются четным числом, верните обещание, отклоненное через 2 секунды, со значением even