Почему в JS-цепочке промисов .then().catch().finally() метод finally выполняется даже при критическом сбое в catch?

Метод `.finally()` в JavaScript спроектирован таким образом, что он **всегда выполняется** — независимо от того, был ли промис выполнен успешно, отклонён или даже если в блоке `.catch()` произошла новая ошибка. Это фундаментальное поведение, заложенное в спецификацию ECMAScript.

## Как работает finally под капотом

Когда вы пишете цепочку `.then().catch().finally()`, каждый из этих методов возвращает **новый промис**. Метод `.finally()` подписывается сразу на оба состояния предыдущего промиса — как на `fulfilled`, так и на `rejected`. Это принципиальное отличие от `.then(onFulfilled, onRejected)`.

Внутренняя логика `.finally(callback)` эквивалентна следующему:

javascript
promise.then(
value => Promise.resolve(callback()).then(() => value),
reason => Promise.resolve(callback()).then(() => { throw reason; })
);

То есть колбэк внутри `.finally()` вызывается в обоих случаях, а затем промис либо пробрасывает исходное значение, либо перебрасывает ошибку дальше.

## Что происходит при ошибке в catch

Если в `.catch()` выбрасывается новая ошибка или возвращается отклонённый промис, то `.catch()` сам возвращает **rejected-промис**. Именно на этот промис и подписан `.finally()`. Поскольку `.finally()` реагирует на оба состояния, он всё равно выполняется.

javascript
Promise.reject(‘initial error’)
.then(() => console.log(‘then — не выполнится’))
.catch(err => {
console.log(‘catch — выполнится’);
throw new Error(‘новая ошибка в catch’); // пробрасываем дальше
})
.finally(() => {
console.log(‘finally — выполнится ВСЕГДА’);
})
.catch(err => {
console.log(‘обработка ошибки из catch:’, err.message);
});

Вывод:

catch — выполнится
finally — выполнится ВСЕГДА
обработка ошибки из catch: новая ошибка в catch

## Важные особенности finally

1. **Не получает аргументов** — колбэк `.finally()` не принимает ни значение, ни причину отклонения. Это сделано намеренно, чтобы подчеркнуть его «нейтральность».

2. **Не изменяет результат** — если `.finally()` возвращает обычное значение, оно игнорируется, и цепочка продолжает передавать исходное значение или ошибку.

3. **Может прервать цепочку** — если внутри `.finally()` выбросить ошибку или вернуть отклонённый промис, то именно эта новая ошибка заменит предыдущую.

## Практическое применение

Метод `.finally()` идеально подходит для **очистки ресурсов**: закрытия соединений с БД, скрытия индикаторов загрузки, освобождения блокировок — всего того, что должно произойти вне зависимости от результата операции. Это JavaScript-аналог блока `finally` в конструкции `try/catch/finally`.


Задайте вопрос нейросети

Не нашли ответ? Спросите ИИ — он подготовит развёрнутую статью.