Лучшая обработка ошибок для классов 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
Компоненты - это строительные блоки. Вы архитектор.
Представьте, что все ваши компоненты организованы в облаке, доступны для вашей команды и синхронизированы во всех ваших проектах. Попробуйте, это бесплатно и с открытым исходным кодом.