Promise API
Last updated
Last updated
Promise (обещание, промис) — объект, представляющий текущее состояние асинхронной операции. Удобный способ организации асинхронного кода.
У промиса есть 2 состояния:
Pending — ожидание, исходное состояние при создании промиса.
Settled — выполнен, которое в свою очередь делится на две категории: fullfilled
— выполнено успешно и rejected
— выполнено с ошибкой.
Вначале промис находится в состоянии ожидания (pending
), после чего он может выполнится успешно (fulfilled
) или с ошибкой (rejected
). Когда промис переходит в состояние выполнен (settled
), с результатом или ошибкой – это навсегда. Грубо говоря, промис - это болванка для данных, значение которых мы не знаем в момент его создания.
Способ использования:
Код, которому надо сделать что-то асинхронно, создаёт обещание и возвращает его.
Внешний код, получив обещание, навешивает на него обработчики.
По завершении процесса асинхронный код переводит обещание в состояние fulfilled
или rejected
. При этом автоматически вызываются обработчики во внешнем коде.
Отличия промиса и callback-функции:
Коллбэки - это функции, обещания это объекты.
Коллбэки передаются в качестве аргументов из внешнего кода во внутренний, обещания возвращаются из внутреннего кода во внешний.
Коллбэки обрабатывают успешное или неуспешное завершение, обещания ничего не обрабатывают.
Коллбэки могут обрабатывать несколько событий, обещания связаны только с одним событием.
Обещания (помимо читабельности, которая является побочным положительным эффектом) используются в основном для композиции, цепочки вызовов и обработки результата и ошибок в отдельных логических ветках.
Обещание создается как экземпляр класса Promise
с одной функцией в качестве аргумента. Вызов конструктора немедленно исполнит функцию fn
, переданную в качестве аргумента. Цель этой функции состоит в информировании экземпляра (промиса), когда событие, с которым он связан, будет завершено.
Параметры передаваемой функции:
resolve(arg)
— функция, которую необходимо вызвать при успешной операции. Переданный в нее аргумент будет значением выполненного промиса.
reject(arg)
— функция, которую необходимо вызвать при ошибке. Переданный в нее аргумент будет значением ошибки которое можно будет обработать.
В переменную promise
будет записан объект обещания в состоянии pending
, а через 2 секунды, после того как будет вызван resolve("success!")
, промис перейдет в состояние fullfilled
.
После того как промис создан, с ним можно работать используя методы then
и catch
, которые доступны через его прототип. Код пишется так, как будто мы размышляем о том, что может произойти если промис выполнится или нет, не думая о временных рамках.
Позволяет выполнить код, в котором можно получить доступ и обработать результат промиса.
В метод then
передаются две функции, которые будут вызваны когда промис перейдет в состояние выполнен (settled).
onResolve(arg)
— будет вызвана при успешном выполнении промиса и получит результат промиса как аргумент (то, что передаем в вызов resolve
).
onReject(arg)
— будет вызвана при выполнении промиса с ошибкой и получит ошибку как аргумент (то, что передаем в вызов reject
).
Если onResolve
и onReject
не содержат сложной логики, их объявляют как инлайн функции в методе then
.
Немного дальше мы узнаем о цепочках промисов, а пока научимся обрабатывать ошибки не в колбеке onReject
метода then
, а в специальном методе catch
. Обрабатывать ошибки очень удобно, используя метод catch
только один раз, в конце цепочки.
Хендлер для обработки состояния reject
, исполнится только если промис исполнится с ошибкой (rejected). onReject(arg)
будет вызвана при выполнении промиса с ошибкой, и получит ошибку как аргумент (то, что передаем в вызов reject).
Создадим обещание, сделаем задержку на 2 секунды, вызовем reject
, имитируя выполнение промиса с ошибкой.
Этот метод может быть полезен, если вы хотите выполнить некоторую обработку или очистку после того, как обещание будет исполнено, независимо от результата.
Позволяет выполнить указанную callback-функцию после того, как обещание будет разрешено (выполнено или отклонено). Позволяет избежать дублирования кода в обработчиках then()
и catch()
. Возвращает обещание.
Функция обратного вызова не получит никаких аргументов, поскольку нельзя точно определить выполнено ли обещание или отклонено. Тут будет выполняться код, который зависит только от времени его исполнения, значение промиса не важно.
Чейнинг (chaining) — возможность строить асинхронные цепочки из промисов. Одна из основных причин существования и активного использования промисов.
Каждый метод then
, результатом своего выполнения, возвращает промис. Его значением будет то, что возвращается из callback-функции onResolve
.
Так как then
возвращает промис, до его выполнения может пройти некоторое время, поэтому оставшаяся часть цепочки будет ждать.
При возникновении ошибки в любом месте цепочки, выполнение всех последующих then
отменяется, а управление передается методу catch
.
Иногда бывают ситуации, когда нам необходимо дождаться выполнения не одного, а сразу набора промисов и только тогда что-то сделать. Бывают и такие ситуации, когда из набора промисов необходимо дождаться выполнения любого одного, проигнорировав остальные.
Статический метод, получает массив промисов и ждет их исполнения, возвращает промис.
При успешном выполнении всех промисов из массива, промис, возвращаемый из Promise.all
, перейдет в состояние settled -> fullfilled
, а его значением будет массив результатов исполнения каждого промиса.
Если в массиве промисов хотя бы один исполнился с ошибкой, то перейдет в состояние settiled -> rejected
, а значением промиса будет ошибка.
Давайте напишем функцию, которая будет принимать текст для resolve
и задержку в мс, а результатом своего выполнения будет возвращать промис. Затем создадим 2 промиса с разным временем задержки.
Статический метод, получает массив промисов и возвращает обещание. Когда хотя бы одно обещание в массиве исполнилось, исполнится возвращаемый промис, а все остальные будут отброшены.
Вызов статического метода Promise.resolve(value)
создаёт успешно выполнившийся промис с результатом value
. Это аналогично new Promise((resolve) => resolve(value))
, только короче. Этот метод используют, когда хотят построить асинхронную цепочку и начальный результат уже есть.
Это можно использовать для того, чтобы заменить callback на цепочку промисов. То есть вместо, того чтобы передавать callback в функцию и надеяться на лучшее, получаем промис и чейним then
, в котором доступен результат работы функции.
Превращается в следующее.
Аналогично Promise.reject(error)
создаёт уже выполнившийся промис, но не с успешным результатом, а с ошибкой error
. Он используется крайне редко, потому что ошибка возникает обычно не в начале цепочки, а в процессе её выполнения.