Делаем промис - шаг 5
Продолжаем писать собственную реализацию промиса. В этом шаге реализуем блок finally.
Что известно о finally
Ключевые свойства:
finallyможно вызвать всегда, внезависиомсти от того, чем завершился промис - успехом или ошибкой.finallyне получает аргументов:valueилиreason.finallyне изменяет результат цепочки. Кроме случаев, когда вfinallyпроизошла ошибка.- Если
finallyвозвращает промис, то цепочка ждет его завершения, но после выполнения возвращается оригинальная причина завершения -valueилиreason.
Начинаем
Будем реализовывать finally, как частный случай then.
Если колбэк не функция
finally должен работать как then, но с прозрачным пропуском значения.
finally(onFinally) {
if (typeof onFinally !== "function") {
return this.then(
value => value,
reason => { throw reason; }
);
}
}
Если onFinally - не функция, то finally ничего не меняет и просто продолжает цепочку:
- Для fulfill-случая возвращаем исходное value.
- Для reject-случая пробрасываем reason.
Если onFinally — функция
finally(onFinally) {
/* .... */
const handleFulfilled = (value) => {
}
const handleReject = (reason) => {
}
return this.then(handleFulfilled, handleReject);
}
Почему handleFulfilled принимает value?
Потому что finally должен вернуть оригинальный результат промиса -
нужно сохранить и вернуть value после выполнения завершающего действия.
Почему handleReject принимает reason?
Аналогично: если исходный промис был отклонён, finally
должен пробросить исходную причину отказа (если только друг сам finally не упадёт).
Реализуем handleFulfilled
const handleFulfilled = (value) => {
// вызываем переданный колбэк
const result = onFinally();
// если result — промис, то ждём его завершения
if (result instanceof MyPromise) {
// после завершения промиса возвращаем исходный value
return result.then(() => value);
}
// если result не промис, просто возвращаем value
return value;
}
Мы обязаны вернуть исходный value, потому finally не меняет успешный результат. А если вернулся промис, то finally
становится асинхронным: просто ждем завершения result.
Аналогично реализуем handleReject
const handleReject = (reason) => {
const result = onFinally();
if (result instanceof MyPromise) {
return result.then(() => {
throw reason;
});
}
throw reason;
};
Если onFinally возвращает промис, мы должны дождаться его выполнения, а после завершения пробросить исходный reason.
Важно обратить внимание, что мы вызываем throw reason именно в блоке then, чтобы бросить отклоненное значение.
Итог
Блок finally готов:
- он не вмешивается в данные
- может быть асинхронным
- сохраняет оригинальное значение или причину ошибки
- только ошибки внутри самого finally могут изменить состояние цепочки
Теперь в вашей реализации промиса поддержаны все основные элементы Promise API: then, catch и finally.