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 станет нулевым?
Проверьте следующий случай: -
- Когда нет thenCb
2. Результат разрешенного/отклоненного промиса будет продолжать передаваться до тех пор, пока он не найдет then/catch с обратным вызовом обработчика.
Обработчик возвращает обещание или примитив
- Если значение является примитивом, оно преобразуется в промис, а затем передается следующему обработчику.
Обещания и микрозадачи
Обработка обещаний всегда асинхронна, так как все действия обещания проходят через внутреннюю очередь «обещаний заданий», также называемую «микрозадача очередь».
Поэтому для реализации того же поведения мы будем использовать queueMicrotask.
В основном задачи queueMicrotask выполняются сразу после того, как текущий стек вызовов становится пустым, прежде чем передать выполнение в цикл обработки событий.
Когда никакая ошибка не обрабатывается — Unhandled Promise
- нет .catch() для обработки отказа от обещания
- Если нет обработчика отклонения, обещание разводит руками и выдает глобальную необработанную ошибку отклонения.
Как это отслеживать в JS?
Событиеunhandledrejection
отправляется в глобальную область действия скрипта, когда отклоняется JavaScriptPromise
, не имеющий обработчика отклонения.
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 выглядит так: