Лучшая обработка ошибок для классов ES / Typescript

Написание собственного декоратора catch.

В целом обработка ошибок в JavaScript - не очень сложная тема, но очень полезная. Когда в течение жизни вашего приложения возникают различные ошибки, сломайте их и оставьте пользователя в недоумении по поводу того, что происходит. 😵

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

Все это можно сделать с помощью try/catch блоков или .catch метода Promise объектов. Все идет нормально. В этом посте мы рассмотрим применение теории на практике при обработке ошибок в приложениях JavaScript. Давайте начнем.

Этот пост предполагает базовый опыт работы с JavaScript.

Все последующие примеры будут показаны для компонентов класса Vue, как типичные ситуации обработки ошибок. Но его можно использовать с любыми классами ES / Typescript!

Двигаемся дальше. ⏬

Совет. Используйте Бит, чтобы инкапсулировать модули / компоненты со всеми их зависимостями и настройками. Делитесь ими в Bit’s Cloud и сотрудничайте со своей командой.

Тебе стоит «попробовать» 🧗‍

В большинстве руководств по обработке ошибок в компонентах Vue (обычно ошибки отображаются пользователю в виде всплывающих сообщений) будет показана реализация, которая выглядит примерно так (пример входа в систему):

Вышеупомянутая реализация верна и отлично выглядит.

  • Делаем запрос API и ждем ответа (токена)
  • Если ошибок нет → обработать ответ
  • Если ошибка обнаружена в loginUser методе → перейдите к блоку catch и обработайте ошибку

Это выглядит легко 😎.

Но что происходит, когда ваше приложение больше, чем небольшой учебник, и содержит много запросов API и прочего, что может вызывать ошибки?

Что ж, это будет выглядеть так:

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

Лучше сделать ваш код более декларативным, описывая что должна выполнять программа, а не просто описывать как для выполнить это.

Итак, мы должны найти способ скрыть эту try/catch логику и найти более элегантное решение для обработки ошибок в нашем приложении Vue.js. Давай попробуем.

Инструменты Vue ⛏

Если вы откроете документацию Vue, вы можете найти свойство глобальных опций с именем errorHandler, которому может быть назначен обработчик неперехваченных ошибок. Это должен быть идеальный вариант для нас, поэтому давайте попробуем:

Большой! 😃 Мы удалили уродливые try/catch блоки и переместили обработку в глобальный errorHandler метод. Запустим приложение:

Хммм… Ошибка от mounted хук ловится, а от created нет. Причина этого (в документации Vue об этом ничего не говорится) в том, что errorHandler не обнаруживает ошибок в асинхронных методах (и собственных обработчиках событий). Внутренне Vue запускает наш created хук как синхронно, не дожидаясь завершения асинхронной операции, когда на самом деле может быть выдана ошибка.

Двигаемся дальше. ⏬

Давай поймаем 🎣

Во время исследования я наткнулся на некоторый код Java и увидел интересную обработку ошибок, изначально поддерживаемую в Java:

Здесь throws IOException сообщает компилятору, что в этом методе может быть выброшено какое-то исключение, и оно должно быть обработано. Было бы здорово реализовать что-то подобное и в нашем случае.

Однако в JavaScript нет собственного механизма, который позволил бы нам реализовать такую ​​функциональность. Верно ... но декораторы есть! 😈

Декоратор - это шаблон программирования, и ниже вы можете увидеть одну из его реализаций:

Для тех, кто слышит об этом впервые (подробнее), функция log в приведенном выше примере является декоратором. Он принимает getData функцию в качестве аргумента и расширяет поведение последней функции с помощью функции ведения журнала.

Декоратор - это структурный шаблон проектирования, который позволяет вам прикреплять новые поведения к объектам, помещая их внутри объектов-оболочек, которые содержат эти поведения.

Но в JavaScript есть собственная реализация декораторов - функция, которая вызывается с префиксом @, за которым сразу следует class, параметр, метод или свойство:

Здесь аннотация @Catch() - наш декоратор. Давайте реализуем это. Ссылаясь на спецификацию ES2016, декоратор - это выражение, которое возвращает функцию и может принимать в качестве аргументов:

  • target - класс, в котором фактически используется этот декоратор;
  • name - имя метода / свойства, к которому применяется этот декоратор;
  • дескриптор свойства - особый объект, который описывает атрибуты свойства.

Базовый декоратор:

Прежде всего, мы сохраняем значение дескриптора в originalMethod. Затем перепишите исходное значение с помощью нашей версии целевой функции, где мы запускаем исходный метод с аргументами из функции-оболочки.

Мы используем .apply, чтобы связать метод с исходным контекстом (this). Вызов исходного метода, заключенного в блок try/catch, и пока просто показывать сообщения о любой ошибке. После этого вернуть дескриптор обратно.

Примечание. await работает как для синхронизирующих, так и для асинхронных функций.

Используя наш собственный декоратор в компоненте:

Запустим приложение:

Оно работает! 🎉 Теперь синхронные (из ловушки mounted) и асинхронные (из ловушки created) правильно обрабатываются, и все это работает только с одной аннотацией.

Вы также можете использовать его для обычных Promise без async/await, тоже подойдет:

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

Здесь мы создали объект catchDecoratorStore с помощью метода asetHandler, который установил для обработчика свойство handler. Затем в блоке catch проверяем, существует ли обработчик, если есть - запускаем, иначе - показываем консольное сообщение по умолчанию.

Теперь мы можем использовать его очень похоже на то, как мы использовали глобальный обработчик ошибок Vue:

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

Но что, если для какого-то метода нужна отдельная логика обработки ошибок? В этом случае мы должны добавить в наш декоратор дополнительную функциональность - возможность принимать аргументы, чтобы использовать его так: @Catch(handler).

Обновленная версия:

Здесь функция Catch принимает обработчик в качестве аргумента и возвращает другую функцию, которая на самом деле является декоратором. Следующее изменение, сделанное здесь (строка 11), проверяет, передан ли обработчик декоратору, и если да, мы вызываем его с объектом ошибки и контекстом (текущий объект, в котором расположен целевой метод ).

Контекст нашего примера - это компонент Vue. Если обработчик не прошел, мы вызовем глобально зарегистрированный обработчик, а в другом случае консольем сообщение.

С этими последними изменениями вы можете писать такие компоненты:

Вот и все, теперь вы можете использовать глобальный обработчик или, в особых случаях, локальные обработчики. Он также работает с любыми классами ES6 / Typescript.

Вы можете найти весь код из этой статьи здесь, клонировать репозиторий и поиграть с исходным кодом.

🧐 Я также написал catch-decorator библиотеку с теми же концепциями, что и в этой статье, но с некоторыми расширенными функциями, которые вы можете найти на Github и установить из NPM:

npm install catch-decorator

Заключение

Обработку ошибок можно выполнять разными способами, и вы должны найти лучшее решение для ваших задач и контекста. Когда проект не очень большой, достаточно переноса обработки ошибок в отдельную makeRequest функцию для ошибок ответа API. Но когда вы используете компоненты класса и хотите создать единый способ обработки ошибок в своем приложении, декораторы могут быть полезны.

Надеюсь, этот пост был полезен 🎓. Если у вас есть какие-либо мысли или вопросы, не стесняйтесь отвечать и оставлять комментарии ниже! Буду рад ответить 🙂. Спасибо.

Доступно ❤️ в блоге Bit

Компоненты - это строительные блоки. Вы архитектор.

Представьте, что все ваши компоненты организованы в облаке, доступны для вашей команды и синхронизированы во всех ваших проектах. Попробуйте, это бесплатно и с открытым исходным кодом.



Учить больше