7 Теория try … catch в JavaScript — разбор на примере кода для Тильда

В JavaScript есть удобная конструкция try..catch, позволяющая обрабатывать ошибки (исключения). Используется он чаще для тех кусков кода, где важен точный результат и ошибки нужно выводить и контролировать. Например такую конструкцию мне нужно было использовать в проекте Свой личный кабинет для интернет — магазина на Тильда

Ниже разберём, как движок ищет обработчик ошибки, что попадает в catch, зачем нужен finally, как писать «правильный» лог и почему try/catch в асинхронных конструкциях спасает от трудно-уловимых сбоев. Всё это проиллюстрируем фрагментом из рабочей функции автопересчёта корзины.

Что такое исключение и как оно работает

Исключением (exception) называют любое «ненормальное» событие, из-за которого текущая точка выполнения не может продолжить работу.

Если интерпретатор сталкивается с «встроенной» ошибкой (ReferenceError, TypeError, кастомная Error), он создаёт объект ошибки и начинает «подниматься» по стеку вызовов, пока не найдёт ближайший catch.

Если ни один обработчик не перехватит исключение, оно дойдёт до корневого контекста:

Такой «вывал» почти всегда считается критической ситуацией: приложение может остаться в неопределённом состоянии.

Базовый синтаксис

try { 
  // код, который может «рвануть» 
} 
catch (error) { 
  // здесь мы «гасим пожар» 
} 
finally { 
  // выполняется В ЛЮБОМ СЛУЧАЕ
}

Блок finally необязателен, но полезен для закрытия соединений, очистки таймеров, скрытия лоадеров и т. д.

Объект Error чем он полезен:

СвойствоЧто в нёмЗачем нужно
nameТип (Error, TypeError, SyntaxError,…)Быстро фильтровать категории ошибок
messageЧеловеко-читаемое описаниеПоказывать юзеру или писать в лог
stackСтрока со стеком вызововТочная точка сбоя + путь до неё

try/catch и асинхронность: подводные камни

Только синхронные исключения ловятся напрямую.

Ошибки, случившиеся «позднее» — в колбэке setTimeout, внутри промиса, в async-функции — требуют отдельного catch (или .catch() у промиса).

В нашем коде startAutoRecalculation таймер синхронный (setInterval), а вот его тело с try/catch исполняется асинхронно каждые 200 мс. Поэтому «защитный купол» нужен внутри колбэка, иначе случайная ошибка в пересчётах зальёт консоль «красным» и прекратит повторную логику.

Разбираем пример по строчкам

let autoRecalcTimer = null; // ⏳ Хранит таймер

function startAutoRecalculation(deliveryDiscount = null, deliveryTypes = []) {
    if (autoRecalcTimer) clearInterval(autoRecalcTimer); // ✅ Если таймер уже есть — сбрасываем

    console.log("✅ [ШАГ 1] Запуск постоянного обновления корзины...");

    autoRecalcTimer = setInterval(() => {
        if (typeof tcart === "undefined") return;

        try {
            if (tcart.promocode && tcart.promocode.message === "OK") {
                console.log("✅ Промокод активен, пересчитываем скидку...");
                recalculateTildaCart();

                // ???? Добавляем повторное применение скидки на доставку
                if (lastAppliedPromocode && lastAppliedPromocode.promocode_d_discount) {
                    applyDeliveryDiscount(
                        lastAppliedPromocode.promocode_d_discount,
                        lastAppliedPromocode.promocode_d_type
                    );
                }
            }
        } catch (error) {
            console.error("❌ [КРИТИЧЕСКАЯ ОШИБКА] Ошибка в `startAutoRecalculation`:", error);
        }
    }, 200);
}

Что даёт такое обёртывание:

⛔️ Ошибка внутри recalculateTildaCart не обрушит цикл setInterval, т. е. корзина продолжит попытки пересчитаться.

Мы получаем полный стек и можем отладить баг.

В критичных случаях кодером легко добавить аварийное восстановление (сбросить промокод, выключить таймер, показать фолбэк-UI).

Рекомендации по правильному catch

ДелайтеНе делайте
Ловите только те ошибки, которые можете обработать осмысленно.Не оборачивайте огромные куски кода «на всякий случай» — потеряете точку падения.
Логируйте error.stack, error.message и любые контекстные данные (ID корзины, userId).Не оставляйте catch (e) {} пустым — «глушители» превращают баги в призраков.
При «частичном» восстановлении возвращайте безопасное значение.Не злоупотребляйте «мутировавшим» глобальным состоянием в catch — можете создать ещё больше хаоса.
Для необрабатываемых сбоёв — throw дальше или отправляйте на мониторинг.Не полагайтесь на try/catch для управления обычным потоком (контроль версий, branching logic).

finally: гарантированная уборка

try {
  startTransaction();
  doStuff();
} catch (e) {
  rollback();
  throw e;      // репроброс
} finally {
  closeConnection(); // вызовется ВСЕГДА
}

Блок выполняется после try и/или catch, даже если там return, throw или ранний выход по ошибке.

В async-функциях он тоже работает, но дожидается завершения await-ов внутри try.

Вывод

try … catch останавливает «каскадную вспышку» ошибок, позволяя приложению остаться живым.

В асинхронных сценариях ставьте его внутрь колбэков/async-функций, иначе исключения «утекут».

Всегда логируйте стек + контекст, различайте «восстановление» и «репроброс».

Используйте finally для гарантированной очистки ресурсов.

Не прячьте баги — ловите ровно те ошибки, которые способны обработать.

Понимание этих принципов делает функцию startAutoRecalculation (и любой другой сложный механизм) устойчивой к неожиданным «падениям», а ваш дебаг — быстрым и предсказуемым.

Как со мной связаться

Если вы не нашли для себя ответ в моих статьях или у вас есть задача, присылайте ваше ТЗ задание под ваш проект.
Пишите https://t.me/vbalakin буду рад помочь с доработкой.

Еще материалы

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Для корректного функционирования, сайт использует cookie, поэтому если вы согласны с политикой конфиденциальности нажмите "Принять"