Вы когда-нибудь задумывались, как Promise работает в JavaScript? Давайте реализуем один и посмотрим, сможем ли мы понять его немного лучше.

Что такое обещание?

Обещание — это объект, который может представлять асинхронную операцию.

Этот объект обещает вернуть результат этой асинхронной операции, будь то успех или неудача. Если эта операция никогда не завершится, она будет ждать до вечности или до тех пор, пока не будет собран мусор.

Что нам нужно, так это средство для сбора результата этой асинхронной операции из промиса, и именно здесь метод then() выходит на первый план.

Затем

Объектом thenable является любой объект с then() реализацией метода. Да, обещания можно использовать.

Из контекста Promise метод then() используется для добавления обработчиков, которые в конечном итоге получат результат асинхронной операции, которую он представляет.

Promise
.resolve( 'result' )
.then( successcallback, failurecallback );

Операция после завершения приводит к тому, что обещание сопоставляется с разрешенным value или отклоненным error.

Если операция завершается успешно, срабатывает successcallback( value ), и разрешенный value передается в качестве аргумента.

Если операция завершается с ошибкой, срабатывает failurecallback( error ), и отклоненный error передается в качестве аргумента.

Таким образом, Promise может иметь только три состояния для представления асинхронной операции, а именно:

  • ожидание: ожидание завершения асинхронной операции.
  • выполнено: операция успешно завершена.
  • отклонено: операция не удалась.

Когда обещание выполняется или отклоняется, оно становится окончательным состоянием обещания, таким образом делая обещание выполненным долго и счастливо…

После того, как обещание выполнено, никакие дальнейшие обновления не могут быть выполнены ни для результата, ни для состояния обещания.

Конструктор обещаний:

Конструктор Promise создает для нас объект обещания:

new Promise( executor )

Он ожидает функцию executor, которая связывает асинхронную активность с обещанием.

Эта функция executor вызывается конструктором Promise с аргументами resolve и reject, которые являются методами, предоставляемыми конструктором Promise для выполнения следующих действий:

  • resolve( data ) : установите состояние обещания на fulfilled и результат как data .
  • reject( error ) : установите состояние обещания на rejected и результат как error.

В зависимости от асинхронной активности функция executor может затем вызвать метод resolve или reject для передачи результата промису.

Созданный Promise имеет внутренние свойства:

  • [[PromiseState]] : текущее состояние промиса.
  • [[PromiseResult]] : содержит разрешенное значение или причину отказа.

Давайте посмотрим на пример:

Создание PromiseCopy:

Начнем с constructor . Мы получаем executor, и нам нужно передать методы resolve и reject, которые являются служебными функциями.

resolve и reject — это статические методы в Promise, которые используются для создания нового Promise, как показано ниже:

Когда эти методы передаются функции executor внутри конструктора Promise, они ведут себя по-разному. Они относятся к экземпляру Promise bind и возвращают undefined .

Обладая вышеуказанными знаниями, давайте продолжим и создадим PromiseCopy:

Обещание.разрешить():

Promise.resolve( value ) получает ответ об успехе, который установлен как #result промиса.

Но если это значение является доступным объектом, Promise.resolve() не устанавливает это значение как #result.

если вы передадите объект, который можно использовать, в Promise.resolve(), он будет продолжать разворачивать результат, рекурсивно вызывая Promise.resolve(), пока не достигнет конечного значения.

Как только Promise.resolve() получает окончательное значение, он обновляет #result обещания и устанавливает состояние выполнено.

Как только обещание выполнено, мы можем отправлять обратные вызовы, с нетерпением ожидающие #result. Давайте напишем метод resolve(), помня о вышеизложенном:

Обещание.отклонить():

Метод reject( reason ) очень похож на метод resolve(), за исключением того, что он не выполняет разворачивание тенаблей. Он идет прямо вперед и устанавливает причину ошибки в #result и устанавливает #state как отклоненный. Кроме того, отправляйте обратные вызовы:

then() и цепочка обещаний:

Промис then( successcallback, failurecallback ) используется для добавления обратных вызовов. Эти обратные вызовы хранятся в экземпляре промиса и запускаются после того, как промис установлен.

Promise.then() также является компонуемым, что означает, что вы можете объединить вызовы then(), и каждый метод then() получает результат предыдущей асинхронной операции.

Это особенно полезно, чтобы избежать ада обратных вызовов, поскольку вы можете связать зависимые асинхронные задачи с помощью цепочки промисов.

then( successcallback, failurecallback ) всегда возвращает новый промис, который разрешается возвращаемым значением либо successcallback, либо failurecallback в зависимости от #state текущего промиса.

Если successcallback или failurecallback возвращает промис, результат разворачивается методом resolve() нового промиса перед выполнением следующего метода then() в цепочке промисов.

Еще одна важная вещь: если эти обратные вызовы не передаются, текущий промис #state и #result перенаправляется на новый промис.

Давайте посмотрим на реализацию then():

Обратите внимание, что запись помещается в #handlers, которая будет активирована, когда текущий промис будет установлен.

Обработчики

Handlers — это массив, который содержит обратные вызовы, прикрепленные с помощью метода then(). Эти обработчики хранятся в ссылке и запускаются асинхронно после установки промиса.

Каждый обработчик содержит методы:

  • success() : выполняется, когда обещание выполнено.
  • fail() : выполняется, когда обещание отклонено.

Мы можем создать метод #dispatchCallback(), чтобы проверить, выполнено ли обещание, и в конечном итоге вызвать обработчики.

Каждый обработчик должен запускаться только один раз после того, как обещание установлено, и впоследствии должен быть удален из ссылки.

Поскольку обработка Promise выполняется в очереди микрозадач, мы можем использовать метод queueMicroTask для асинхронного запуска обработчиков как микрозадачи.

У нас также будет закрытое свойство #queued в качестве проверки, чтобы избежать дублирования триггеров:

Ошибка обещания журнала

Обещание, если отклонено, регистрирует общую ошибку, если для обработки ошибки не указан failurecallback.

Хотя в цепочке промисов отклонено промис будет пересылаться handler до тех пор, пока не достигнет промиса без #handlers. По сути, это может быть наша проверка для запуска журнала ошибок, как это было сделано выше в #dispatchCallback().

ловить():

Мы также можем присоединить failurecallback, используя метод catch(). Однако нам также необходимо передать значение следующему методу then(), если обещание выполнено.

Для этого мы можем повторно использовать метод then(), а также передать undefined как successcallback. Довольно простое право:

окончательно():

finally( finallycallback ) реализуется независимо от того, выполняется или отклоняется Promise. Но это не так просто, как просто передать finallycallback в then( finallycallback, finallycallback ) .

Особенность finally() заключается в том, что #result текущего промиса не передается finallycallback. Эти #state и #result текущего промиса передаются новому промису, возвращенному finally().

Если finallycallback является асинхронным, то есть если он возвращает Promise или thenable, следующий обратный вызов в цепочке Promise будет запущен только после завершения этой операции.

В некотором смысле, finallycallback — это просто некоторая промежуточная задача, стоящая между цепочкой промисов, которая должна быть успешной.

Потому что, если в finallycallback есть ошибка, новое обещание, возвращаемое finally(), отклоняется с этой ошибкой.

Давайте посмотрим на реализацию:

Заключение:

Понимание того, как работает Promise, может быть чрезвычайно полезным при реализации асинхронных задач в JavaScript. Я надеюсь, что эта статья поможет вам создать свой собственный промис, а затем асинхронно дождаться его.

Вы также можете перейти по ссылке ниже, чтобы поиграть с PromiseCopy:



Дальнейшее чтение: