new Promise((resolve, reject)=> { .... })

Здесь мы видим, что Promise принимает функцию обратного вызова, которая дополнительно принимает два аргумента: метод разрешения и метод отклонения.

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

  • Начиная с Custom Promise, нам нужен конструктор, который принимает обратный вызов.
  • Этот код конструктора обернут в try…catch, так как код промиса выдает ошибку всякий раз, когда это происходит.

Обещание будет иметь: -

  • значение, с которым он будет разрешать или отклонять.
  • состояние, которое может быть: ожидающим, выполненным или отклоненным.
  • массив thenCbs для хранения всех функций .then в обещании
  • массив catchCbs для хранения всех функций .catch в обещании
  • #onSuccess — приватный метод, переданный в качестве первого обратного вызова в функции-исполнителе
  • #onFail — закрытый метод, переданный как второй обратный вызов в функции-исполнителе — вызывается при возникновении какой-либо ошибки
  • #onSuccessBinded — привязать onSuccess, чтобы контекст не терялся
  • #onFailBinded — привязка onFail во избежание потери контекста

Код конструктора обернут в try… catch, чтобы он мог обработать ошибку с помощью метода onFail.

Когда обещание разрешается или отклоняется

Разрешение Promise обрабатывается функцией onSuccessBinded, а отклонение — функцией onFailBinded.

Оба этих метода будут принимать значение, с которым обещание разрешается или отклоняется.

Здесь метод #onSucess делает следующее:

  • Проверяет, находится ли промис уже в состоянии выполнено или отклонено, и возвращается. Уже урегулированный не будет обработан.
  • Устанавливает значение, которое будет передано в .thenCb.
  • Устанавливает состояние выполнено, чтобы избежать дальнейшего разрешения и отклонения, которые будут вызываться в одном и том же промисе.
  • Запускает обратные вызовы в массиве .thenCbs для значения, с которым разрешается промис.

Обещание уже обработано в данном случае: -

Метод runcallbacks выглядит так: -

Он делает следующее:

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

Методы прототипа

С прототипом Promise связано всего три метода:

.затем(затемCb, catchCb)

  • вызывается, когда обещание разрешается.
  • Он возвращает обещание для поддержки цепочки обещаний.
  • Он принимает два аргумента — функции обратного вызова для случаев успеха и неудачи Promise.

.catch(catchCb)

  • занимается отклоненными делами.
  • Он возвращает обещание для поддержки цепочки обещаний.
  • Он внутренне вызывает .then(undefined, catchCb)

.finally(cb)

  • Он выполняет обратный вызов cb.
  • Это не влияет на состояние исходного обещания.
  • В отличие от Promise.resolve(2).then(() => 77, () => {}) (который вернет разрешенное обещание с результатом 77), Promise.resolve(2).finally(() => 77) вернет новое разрешенное обещание с результатом 2.
  • Но и Promise.reject(3).finally(() => {throw 99}), и Promise.reject(3).finally(() => Promise.reject(99)) отклонят возвращенное обещание по причине 99.

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

Чтобы поддерживать цепочку промисов, then(), catch() и finally() возвращают промис, по которому эти методы могут быть вызваны (сцеплены).

  • then() теперь возвращает обещание для дальнейшего связывания.
  • он будет нажимать все обратные вызовы
  • если thenCb отсутствует, разрешается с результатом предыдущего обещания => передает тот же результат.
  • если присутствует thenCb, разрешается с результатом thenCb, вызванного в результате => resolve(thenCb(result))

Когда тогда Cb станет нулевым?

Проверьте следующий случай: -

  1. Когда нет thenCb

2. Результат разрешенного/отклоненного промиса будет продолжать передаваться до тех пор, пока он не найдет then/catch с обратным вызовом обработчика.

Обработчик возвращает обещание или примитив

  • Если значение является примитивом, оно преобразуется в промис, а затем передается следующему обработчику.

Обещания и микрозадачи

Обработка обещаний всегда асинхронна, так как все действия обещания проходят через внутреннюю очередь «обещаний заданий», также называемую «микрозадача очередь».

Поэтому для реализации того же поведения мы будем использовать queueMicrotask.

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

Когда никакая ошибка не обрабатывается — Unhandled Promise

  • нет .catch() для обработки отказа от обещания
  • Если нет обработчика отклонения, обещание разводит руками и выдает глобальную необработанную ошибку отклонения.

Как это отслеживать в JS?
Событие unhandledrejection отправляется в глобальную область действия скрипта, когда отклоняется JavaScript Promise, не имеющий обработчика отклонения.

window.addEventListener("unhandledrejection", (event) => {
  console.warn(`UNHANDLED PROMISE REJECTION: ${event.reason}`);
});

Обработка необработанных промисов в Custom Promise Polyfill

Статические методы Promise

Существует 6 статических методов Promise:

  • Обещание.все
  • Promise.allSettled
  • Обещание.гонка
  • Обещание.любое
  • Обещание.разрешение
  • Обещание.отклонить

Здесь мы обсудим Promise.resolve и Promise.reject.

Обещание.разрешение

Promise.resolve(value) создает разрешенное обещание с результатом value.

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

Promise.reject(error) создает отклоненное обещание с error.

Полный Polyfill выглядит так:

Источники

Упрощенная веб-разработка