try...catch...finally

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

Зачем нужно

Без обработки ошибок любое необработанное исключение прерывает выполнение скрипта. try...catch позволяет продолжить работу программы при ошибке, вывести понятное сообщение пользователю или выполнить очистку ресурсов. finally гарантирует завершающие действия (закрытие соединения, скрытие спиннера) вне зависимости от исхода.

Где используется

  • Обработка ошибок при работе с API (fetch, JSON-парсинг)
  • Работа с localStorage, файлами, внешними зависимостями
  • Гарантированная очистка ресурсов в finally
  • Валидация и выброс кастомных ошибок через throw

Синтаксис и поведение

try {
  // Код, который может выбросить ошибку
  const data = JSON.parse('невалидный JSON'); // SyntaxError
} catch (error) {
  // error — объект Error
  console.error(error.name);    // "SyntaxError"
  console.error(error.message); // "Unexpected token..."
  console.error(error.stack);   // стек вызовов
} finally {
  // Выполняется всегда: и при ошибке, и без неё
  console.log('Завершено');
}

Примеры

Работа с fetch

async function fetchUser(id) {
  let spinner;
  try {
    spinner = showSpinner;
    const response = await fetch(`/api/users/${id}`);

    if (!response.ok) {
      throw new Error(`HTTP ${response.status}: ${response.statusText}`);
    }

    return await response.json();
  } catch (error) {
    console.error('Ошибка загрузки пользователя:', error.message);
    showError('Не удалось загрузить данные');
    return null;
  } finally {
    hideSpinner(spinner); // выполнится даже при ошибке
  }
}

Кастомные ошибки через throw

class ValidationError extends Error {
  constructor(field, message) {
    super(message);
    this.name = 'ValidationError';
    this.field = field;
  }
}

function validateAge(age) {
  if (typeof age !== 'number') throw new TypeError('Возраст должен быть числом');
  if (age < 0 || age > 150) throw new ValidationError('age', 'Некорректный возраст');
  return true;
}

try {
  validateAge(-5);
} catch (error) {
  if (error instanceof ValidationError) {
    console.log(`Поле ${error.field}: ${error.message}`);
  } else if (error instanceof TypeError) {
    console.log('Ошибка типа:', error.message);
  } else {
    throw error; // пробросить неожиданные ошибки
  }
}

JSON.parse с fallback

function safeParseJson(str, fallback = null) {
  try {
    return JSON.parse(str);
  } catch {
    // catch без переменной (ES2019+)
    return fallback;
  }
}

const data = safeParseJson(localStorage.getItem('config'), {});

finally для очистки

function openConnection() { /* ... */ }
function closeConnection(conn) { /* ... */ }

async function doWork() {
  const conn = openConnection;
  try {
    await processData(conn);
  } finally {
    closeConnection(conn); // закроем соединение в любом случае
  }
}

Частые ошибки

  • Поглощение ошибок без логирования — пустой catch скрывает проблему; всегда логируйте или перебрасывайте ошибки.
  • return в finally перекрывает return в try — если в finally есть return, он всегда побеждает; избегайте return в finally.
  • try...catch не ловит асинхронные ошибки — ошибки в setTimeout или промисах без await не перехватываются синхронным catch; используйте async/await или .catch.

Связанные темы

Ресурсы