Перейти к содержимому
Теория · Ошибки · урок 3 из 10

finally

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

В программе так бывает с переменными, которые код меняет на время работы. Когда в блоке try брошена ошибка, строки после неё не выполняются. Если среди них была важная строка, она тоже пропадёт. Функция load разбирает текст JSON и на это время ставит флаг — переменную loading со значением true:

JavaScript
let loading = false;

function load(text) {
    loading = true;
    const data = JSON.parse(text);
    loading = false;
    return data;
}

try {
    load("{ bad");
} catch (e) {
    console.log("caught"); // caught
}
console.log(loading); // true

Давайте разберём, что случилось:

  1. load записала в loading значение true.
  2. JSON.parse бросил SyntaxError на неверном тексте.
  3. Функция прервалась раньше строки loading = false.
  4. Ошибку поймал catch снаружи и напечатал caught.
  5. Флаг остался true: программа считает, что загрузка всё ещё идёт.

Блок finally

После try и catch можно добавить блок finally. Это и есть «положить калькулятор на место»: блок выполняется при любом исходе. Таких исходов три:

  • try прошёл без ошибок;
  • ошибку поймал catch;
  • ошибка вышла из функции наружу.

Поэтому в finally пишут то, что должно случиться в любом случае. Если сработал catch, finally выполняется после него.

Чаще всего finally отменяет то, что код сделал на время работы: сбрасывает флаг или возвращает счётчику прежнее значение.

JavaScript
function load(text) {
    loading = true;
    try {
        return JSON.parse(text);
    } finally {
        loading = false;
    }
}

Разберём, что изменилось:

  1. loading = true по-прежнему стоит в начале функции.
  2. Разбор текста переехал в try, а сброс флага — в finally.
  3. Блока catch здесь нет: ошибку из JSON.parse ловит вызывающий код, как в первом примере.
  4. Прежде чем ошибка выйдет из функции, finally сбросит флаг, и console.log(loading) напечатает false.

Пара try и finally без catch допустима, а try без catch и без finally — нет.

finally и return

finally не пропускается и тогда, когда try завершается через return. Функция запоминает возвращаемое значение, затем выполняет finally, и только после этого вызывающий код получает значение:

JavaScript
function count() {
    try {
        console.log("try"); // try
        return 1;
    } finally {
        console.log("finally"); // finally
    }
}

console.log(count()); // 1

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

  1. try печатает try.
  2. return 1 запоминает результат, но функция ещё не закончилась.
  3. finally печатает finally.
  4. Только теперь внешний console.log получает 1 и печатает его последним.

Этот порядок часто проверяют на собеседовании: «Выполнится ли finally, если в try стоит return?» Выполнится, и до того, как вызывающий код получит значение.

Здесь аналогия со стажёром перестаёт работать: человек может забыть про калькулятор, а finally не забывает никогда. Даже return в try его не отменяет.

Частая ошибка

В finally написали return:

JavaScript
function load(text) {
    try {
        return JSON.parse(text);
    } finally {
        return null; // ✗
    }
}

Сообщения об ошибке не будет, но функция сломана. return в finally заменяет значение из try, поэтому load('{"a":1}') возвращает null. Брошенную ошибку он тоже отменяет: для "{ bad" функция тихо вернёт null, и catch снаружи ничего не получит. В finally оставляют только действия вроде loading = false, без return.

Главное

  • finally выполняется при любом исходе: без ошибки, после catch и когда ошибка выходит наружу.
  • В finally возвращают на место то, что код поменял на время работы, например сбрасывают флаг.
  • После return в try блок finally выполняется до того, как вызывающий код получит значение.
  • return в finally заменяет результат и отменяет брошенную ошибку, поэтому его там не пишут.

Подпишись, чтобы пройти курс

Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.

Войти другим способом