Finally: примеры (JAVASCRIPT)

Использование finally в JavaScript на практике
Раздел: Обработка ошибок, Блоки
finally(onFinally: function): Promise

Основы функции finally

Конструкция finally не является самостоятельной функцией, а представляет собой блок, который завершает составную инструкцию try...catch...finally. Этот блок кода выполняется всегда, независимо от того, было ли выброшено исключение в блоке try или нет.

Основное назначение finally — разместить код очистки ресурсов, который должен быть выполнен в любом случае. Например, закрытие файловых дескрипторов, очистка таймеров или сброс состояния.

Блок finally не принимает аргументов и не возвращает значений явным образом. Если в блоке finally есть оператор return, throw или break, он переопределит результат выполнения всего блока try...catch.

Простые примеры использования

Базовый пример с успешным выполнением:

try {
  console.log('Попытка выполнить код');
  // Код без ошибок
} catch (err) {
  console.error('Ошибка:', err.message);
} finally {
  console.log('Этот блок выполнится всегда');
}
// Вывод:
// Попытка выполнить код
// Этот блок выполнится всегда
Попытка выполнить код
Этот блок выполнится всегда

Пример с возникновением ошибки:

try {
  console.log('Попытка');
  throw new Error('Искусственная ошибка');
} catch (err) {
  console.error('Перехвачено:', err.message);
} finally {
  console.log('Finally выполнен после ошибки');
}
Попытка
Перехвачено: Искусственная ошибка
Finally выполнен после ошибки

Похожие конструкции в JavaScript

Прямой альтернативы блоку finally не существует, так как его поведение уникально. Однако можно рассмотреть:

  • Promise.prototype.finally() — метод промисов, выполняющийся при их разрешении или отклонении. Аналогичен по логике, но работает с асинхронным кодом.
  • try...catch без finally — если очистка ресурсов не требуется, можно обойтись только этими блоками.
  • Использование деструктуризации и автоматического управления ресурсами через обертки, но это требует дополнительной реализации.

Блок finally предпочтительнее использовать именно для гарантированного кода очистки в синхронных операциях.

Аналоги в других языках

Python использует блок finally в конструкции try...except...finally, который работает аналогично:

try:
    print("Попытка")
    raise ValueError("Ошибка")
except ValueError as e:
    print(f"Перехвачено: {e}")
finally:
    print("Выполняется всегда")
Попытка
Перехвачено: Ошибка
Выполняется всегда

PHP также имеет блок finally с похожей семантикой:

try {
    echo "Попытка\n";
    throw new Exception("Исключение");
} catch (Exception $e) {
    echo "Перехвачено: " . $e->getMessage() . "\n";
} finally {
    echo "Finally блок\n";
}
Попытка
Перехвачено: Исключение
Finally блок

C++ использует для подобных целей механизм RAII (получение ресурса есть инициализация) и деструкторы, что является идиоматическим подходом для этого языка, а не отдельным блоком кода.

Типичные ошибки

1. Попытка использовать finally без try или catch приводит к синтаксической ошибке:

finally {
  console.log('Одинокий finally');
}
// SyntaxError: Illegal finally clause
SyntaxError: Illegal finally clause

2. Непонимание порядка выполнения при наличии return в finally. Значение из finally переопределяет возвращаемое значение:

function test() {
  try {
    return 'из try';
  } catch(err) {
    return 'из catch';
  } finally {
    return 'из finally'; // Это значение будет возвращено
  }
}
console.log(test());
из finally

3. Игнорирование ошибок, выброшенных в блоке finally, которые могут перекрыть оригинальное исключение.

Изменения в последних версиях

Сам блок finally как часть try...catch существует с первых версий ECMAScript и не претерпел значительных синтаксических изменений. Однако, с появлением ECMAScript 2018 был добавлен метод Promise.prototype.finally() для асинхронных операций, который реализует схожую логику гарантированного выполнения для промисов. Это расширило концепцию finally на асинхронный код.

Расширенные примеры

Использование для гарантированного закрытия ресурса:

Пример javascript
let resource = { open: true, data: 'some data' };

try {
  console.log('Работа с ресурсом:', resource.data);
  // Имитация ошибки в процессе работы
  throw new Error('Ошибка при работе');
} catch (err) {
  console.error('Произошла ошибка:', err.message);
} finally {
  // Гарантированное освобождение ресурса
  resource.open = false;
  console.log('Ресурс закрыт:', resource.open);
}
console.log('Состояние после:', resource);
Работа с ресурсом: some data
Произошла ошибка: Ошибка при работе
Ресурс закрыт: false
Состояние после: { open: false, data: 'some data' }

Finally с управлением потоком выполнения:

Пример javascript
function complexFlow() {
  let counter = 0;
  try {
    counter++;
    if (Math.random() > 0.5) {
      throw new Error('Случайная ошибка');
    }
    return 'Успех';
  } catch (e) {
    counter++;
    throw e; // Пробрасываем ошибку дальше
  } finally {
    console.log(`Счетчик операций: ${counter}`);
  }
}

try {
  console.log(complexFlow());
} catch (e) {
  console.log('Внешний перехват:', e.message);
}
Счетчик операций: 1
Успех
// ИЛИ
Счетчик операций: 2
Внешний перехват: Случайная ошибка

Вложенные блоки try-catch-finally:

Пример javascript
try {
  console.log('Внешний try');
  try {
    console.log('Внутренний try');
    throw new Error('Внутренняя ошибка');
  } finally {
    console.log('Внутренний finally');
  }
} catch (err) {
  console.log('Внешний catch:', err.message);
} finally {
  console.log('Внешний finally');
}
Внешний try
Внутренний try
Внутренний finally
Внешний catch: Внутренняя ошибка
Внешний finally

JS finally function comments

En
Finally Schedules a function to be called when a promise is settled (fulfilled or rejected).