Каждый разработчик хотя бы раз ловил себя на мысли: «Почему это вообще работает?» или «Что за черт, почему упало?». Ошибки в JavaScript — как внезапный дождь: вроде бы предсказуемо, но всегда некстати. Но если вы научитесь их обрабатывать, то сможете превратить хаос в порядок. В этой статье я расскажу, как сделать ваши приложения устойчивыми к сбоям, даже если что-то идет не по плану.

Digital-студия WNDER

Почему ошибки — это не приговор, а часть жизни

Давайте сразу договоримся: ошибки в коде — это нормально. Даже у топ-разработчиков они случаются. Главное — не избегать их, а уметь вовремя поймать и корректно отреагировать. В JavaScript есть несколько способов обработки ошибок: от простого try...catch до использования промисов и асинхронных функций. Каждый из них — как инструмент в ящике: нужен для своей задачи.

Представьте, что вы готовите ужин. Если вы пересолили суп — это ошибка. Но вы же не выбрасываете кастрюлю, а просто добавляете воды. Так и в программировании: ошибка — это сигнал, что что-то пошло не так, и ваша задача — понять, что именно, и исправить ситуацию.

Основы: try...catch, throw и finally

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

try {
  // Пытаемся выполнить опасный код
  let data = JSON.parse('{"name": "WNDER"}');
  console.log(data.name);
} catch (error) {
  // Ловим ошибку и выводим сообщение
  console.error('Что-то пошло не так:', error.message);
} finally {
  // Этот блок выполнится всегда, даже если ошибки не было
  console.log('Операция завершена');
}

Здесь try — это попытка, catch — обработка ошибки, а finally — то, что происходит в любом случае. Это как в жизни: вы пытаетесь открыть дверь, если ключ не подходит — вы ругаетесь (catch), но в итоге все равно идете к соседу за запасным (finally).

А еще есть оператор throw — он позволяет вам самим создавать ошибки. Например, если пользователь ввел некорректные данные:

function divide(a, b) {
  if (b === 0) {
    throw new Error('Деление на ноль запрещено');
  }
  return a / b;
}

try {
  console.log(divide(10, 0));
} catch (error) {
  console.log('Ошибка:', error.message);
}

Тут мы сами говорим: «Стоп, так нельзя». Это помогает избежать непредсказуемого поведения программы.

Асинхронные ошибки: работа с промисами и async/await

Современный JavaScript — это мир асинхронности. Запросы к серверу, чтение файлов, таймеры — все это работает не сразу, а через какое-то время. И тут ошибки прячутся по-другому. Если в синхронном коде мы просто оборачиваем все в try...catch, то с промисами нужно быть внимательнее.

Для промисов есть метод .catch(). Например:

fetch('https://api.example.com/data')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Ошибка запроса:', error));

А если вы используете async/await, то можно снова вернуться к привычному try...catch:

async function getData() {
  try {
    let response = await fetch('https://api.example.com/data');
    let data = await response.json();
    console.log(data);
  } catch (error) {
    console.error('Ошибка:', error);
  }
}

Обратите внимание: await работает только внутри async-функций. И если вы забудете обернуть вызов в try...catch, ошибка может «упасть» молча, и вы будете долго искать причину.

Продвинутые техники: кастомные ошибки и глобальный перехват

Иногда стандартных ошибок мало. Например, вы хотите различать ошибки валидации и ошибки сети. Для этого можно создавать свои классы ошибок:

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

function validateUser(user) {
  if (!user.name) {
    throw new ValidationError('Имя обязательно');
  }
}

try {
  validateUser({});
} catch (error) {
  if (error instanceof ValidationError) {
    console.log('Проблема с данными:', error.message);
  } else {
    console.log('Неизвестная ошибка:', error);
  }
}

Это как разные типы сигналов на приборной панели: красный — критично, желтый — предупреждение. Так вы сразу понимаете, что случилось.

Еще есть глобальный перехват ошибок — window.onerror и process.on('uncaughtException') для Node.js. Но это уже для крайних случаев, когда ошибка происходит вне вашего контроля, например, в сторонней библиотеке.

Практические советы и лайфхаки

Теперь давайте подведем итоги и дадим несколько советов, которые пригодятся в реальной работе.

Правило: Всегда обрабатывайте ошибки там, где они могут возникнуть. Не надейтесь, что «авось пронесет». Если вы делаете запрос к API — оберните его в try/catch. Если читаете файл — проверьте, существует ли он. Это как ремень безопасности: пристегиваетесь не потому, что планируете аварию, а потому что она может случиться.

Вот еще несколько лайфхаков:

  • Используйте console.error вместо console.log для ошибок — так их проще найти в логах.
  • Логируйте ошибки в специальный сервис (например, Sentry), чтобы знать о проблемах до того, как о них сообщат пользователи.
  • Не оставляйте пустые catch блоки — всегда хотя бы выводите ошибку в консоль.
  • Для асинхронных функций используйте try...catch внутри, а не снаружи — так вы не пропустите ошибку.

Также полезно знать, какие ошибки бывают. Вот небольшая таблица:

Тип ошибкиКогда возникаетПример
SyntaxErrorСинтаксическая ошибка в кодеПропущена скобка
TypeErrorНеверный тип данныхОбращение к свойству undefined
ReferenceErrorНесуществующая переменнаяИспользование переменной до ее объявления
RangeErrorЧисло вне допустимого диапазонаМассив с отрицательной длиной
URIErrorНекорректный URIНеправильный encodeURI

И помните: ошибки — это не враги, а подсказки. Они говорят вам, где нужно быть внимательнее. Если вы научитесь их слушать, ваш код станет крепче, а пользователи — счастливее.

Итоги

Итак, мы разобрали, как обрабатывать ошибки в JavaScript. Главное — не игнорировать их, а использовать все доступные инструменты: try...catch, throw, промисы и async/await. Не забывайте про кастомные ошибки и глобальный перехват — они помогают сделать код понятнее и устойчивее.

В итоге вы получите приложение, которое не развалится при первой же проблеме, а аккуратно сообщит о ней и продолжит работать. Это и есть признак профессионализма — не писать код без ошибок, а уметь с ними справляться.

Студия WNDER