Независимо от вашего уровня квалификации, ошибки или исключения являются частью жизни вашего разработчика приложения. Несоответствие веб -разработки оставляет много мест, где ошибки могут и произошли. Ключ к решению состоит в том, чтобы справиться с любыми непредвиденными (или прогнозируемыми ошибками) для управления опытом пользователя. С JavaScript существует множество технических и языковых функций, которые можно использовать для правильного решения любой проблемы.
Опасно обрабатывать ошибки в JavaScript. Если вы верите в закон Мерфи, то, что пойдет не так, в конечном итоге пойдет не так! В этой статье я копаюсь в обработке ошибок в JavaScript. Я буду включать некоторые подводные камни и хорошие практики. Наконец, мы обсудим асинхронную обработку кода и Ajax.
Я думаю, что модель JavaScript, управляемая событиями, добавляет богатый смысл этому языку. Я думаю, что нет никакой разницы между двигателем, управляемым событием и механизмом отчетности об ошибках такого рода браузера. Всякий раз, когда возникает ошибка, она эквивалентна броске события в определенный момент времени. Теоретически, мы можем обрабатывать события бросания ошибок в JavaScript, как мы обрабатываем обычные события. Если это звучит странно для вас, сосредоточьтесь на том, чтобы начать путешествие ниже. Эта статья нацелена только на JavaScript клиента.
Пример
Примеры кода, используемые в этой статье, могут быть получены на GitHub. Текущая страница выглядит так:
Нажатие каждой кнопки выбросит ошибку. Это имитирует исключение типа TypeError. Ниже приведено определение и модульный тест такого модуля.
function error () {var foo = {}; вернуть foo.bar ();}Во -первых, эта функция определяет пустой объект Foo. Обратите внимание, что метод bar () нигде не определяется. Мы используем модульные тесты, чтобы убедиться, что это бросает ошибку.
It ('Throws A TypeError', function () {должен. throws (target, typeerror);});В этом модульном тесте используются тестовые утверждения из библиотек Mocha и должны. Mocha - это работающая тестовая структура, и должен. JS является библиотекой утверждений. Если вы не очень знакомы с ними, вы можете просматривать их документы в Интернете бесплатно. Тестовый пример обычно начинается с него («Описание») и заканчивается прохождением или сбоем утверждения в должности. Преимущество использования этой структуры заключается в том, что он может быть проверен на единицу в узле, а не в браузере. Я предлагаю вам серьезно относиться к этим тестам, потому что они подтверждают много ключевых основных концепций в JavaScript.
Как показано выше, ошибка () определяет пустой объект, а затем пытается вызвать метод в нем. Поскольку метод bar () не существует в этом объекте, он вызовет исключение. Поверьте мне, на динамических языках, таких как JavaScript, любой может допустить такие ошибки.
Плохая демонстрация
Давайте посмотрим на плохие методы обработки ошибок. Я собираюсь абстрагировать неправильное действие и связать его с кнопкой. Вот как выглядит модульный тест обработчика:
функция BadHandler (fn) {try {return fn (); } catch (e) {} return null;}Эта функция обработки получает функцию обратного вызова FN в качестве зависимости. Затем функция называется внутри обработчика. Этот модульный тест примеров, как использовать этот метод.
It ('возвращает значение без ошибок', function () {var fn = function () {return 1;}; var result = target (fn); result. должен. должен (результат) .equal (null);});Как вы можете видеть, если возникает ошибка, этот странный способ обработки возвращает нуль. Эта функция обратного вызова fn () будет указывать на юридический метод или ошибку. Событие обработки щелчков ниже завершает остальные.
(function (Handler, Bomb) {var badbutton = document.getElementbyId ('bad'); if (badbutton) {badbutton.addeventlistener ('click', function () {Handler (Bomb); console.log ('Imagine, получение продвижения для скрытия ошибок');});Плохо, что то, что я только что получил, было ноль. Это сделало меня очень смущенным, когда я думал о определении того, что случилось. Эта стратегия молчания, когда возникают ошибки, охватывает каждую ссылку от дизайна пользовательского опыта до повреждения данных. Последовала разочаровывающая сторона, что мне пришлось часами отладки, но не мог видеть ошибку в кодовом блоке Try-Catch. Эта странная обработка скрывает все ошибки в коде, и она предполагает, что все нормально. Это может быть гладко в некоторых командах, которые не обращают внимания на качество кода. Тем не менее, эти скрытые ошибки в конечном итоге заставит вас тратить часы отладки вашего кода. В многослойном решении, которое опирается на стек вызовов, можно определить, откуда поступает ошибка. Может быть, уместно молча обрабатывать попытку в редких случаях. Но если вы столкнетесь с ошибкой, вы столкнетесь с ней, что не является хорошим решением.
Эта стратегия молчания побудит вас лучше справиться с ошибками в вашем коде. JavaScript предоставляет более элегантный способ решения проблемы этого типа.
Не читаемое решение
Давайте продолжим, давайте посмотрим на трудный для понимания подход. Я пропущу плотно связанную часть с DOM. Эта часть ничем не отличается от плохого подхода, который мы только что видели. Основное внимание уделяется части модульного теста ниже, которая обрабатывает исключения.
функция UglyHandler (fn) {try {return fn (); } catch (e) {throw error ('новая ошибка'); }} it ('Возвращает новую ошибку с ошибками', function () {var fn = function () {Throw New TypeError ('Type Error');}; sup.Throws (function () {target (fn);}, ошибка);});Существует хорошее улучшение по сравнению с плохим лечением только сейчас. Исключение добавлено в стек вызовов. Что мне нравится, так это то, что ошибки освобождаются из стека, что очень полезно для отладки. Когда исключение брошено, интерпретатор рассмотрит следующую функцию обработки на уровне в стеке вызовов. Это предоставляет много возможностей для обработки ошибок на верхнем уровне стека вызовов. К сожалению, потому что он сложная ошибка, я не вижу исходной информации об ошибках. Поэтому я должен посмотреть вдоль стека вызовов и найти самое примитивное исключение. Но, по крайней мере, я знаю, что происходит ошибка, когда исключение брошено.
Хотя эта нечитаемая обработка ошибок является безобидной, она затрудняет понимание кода. Давайте посмотрим, как браузер обрабатывает ошибки.
Звоните в стек
Затем один из способов добавить исключение - добавить попробовать ... поймать код блок на верхнем уровне стека вызовов. Например:
функция main (бомба) {try {bomb (); } catch (e) {// обрабатывать все ошибки}}Но помните, что я сказал, что браузеры управляются событиями? Да, исключение в JavaScript - не что иное, как событие. Интерпретатор останавливает программу в контексте, где в настоящее время происходит исключение, и бросает исключение. Чтобы подтвердить это, ниже приводится глобальная функция обработки событий Onerror, которую мы можем увидеть. Похоже, это:
window.addeventListener ('error', function (e) {var error = e.error; console.log (erry);});Этот обработчик событий улавливает ошибки в среде выполнения. События ошибок будут создавать различные ошибки в разных местах. Смысл этого подхода заключается в центральной обработке ошибок в коде. Как и другие события, вы можете использовать глобальный обработчик для обработки различных ошибок. Это делает ошибки, обрабатывающую только одну цель, если вы придерживаетесь принципов солидной (единственной ответственности, открытой закрытой, замены Лискова, сегрегации интерфейса и инверсии зависимости). Вы можете зарегистрировать функции обработки ошибок в любое время. Интерпретатор выполняет эти петли функций. Код освобожден от заявлений, заполненных Try ... поймать и становится легко отлаживать. Ключом к этому подходу является обработка ошибок, которые происходят, как и обычные события JavaScript.
Теперь есть способ отобразить стек вызовов с глобальной функцией обработки. Что мы можем с этим сделать? В конце концов, мы должны использовать стек вызовов.
Запишите стек вызовов
Стек вызовов очень полезен при обработке исправлений ошибок. Хорошей новостью является то, что браузер предоставляет эту информацию. Несмотря на то, что атрибут стека объекта ошибки в настоящее время не является стандартным, этот атрибут обычно поддерживается в новых браузерах.
Итак, круто, что мы можем сделать, это распечатать на сервер:
window.addeventListener ('error', function (e) {var stach = e.error.stack; var message = e.error.tostring (); if (stach) {message + = '/n' + стек;} var xhr = new xmlhttprequest (); xhr.open ('post', '/lod', true);Это может быть не очевидно в примере кода, но этот обработчик событий будет вызван предыдущим кодом ошибки. Как упоминалось выше, у каждого обработчика есть одна цель, которая делает код сухим (не повторяйте себя, не многократно делая колесо). Что меня интересует, так это как захватить эти сообщения на сервере.
Вот скриншот времени выполнения узла:
Стек вызовов очень полезен для отладки кода. Никогда не недооценивайте роль стека вызовов.
Асинхронная обработка
О, это довольно опасно обращаться с асинхронным кодом! JavaScript выводит асинхронный код из текущей среды выполнения. Это означает, что есть проблема с оператором Try ... Catch ниже.
function asynchandler (fn) {try {settimeout (function () {fn ();}, 1); } catch (e) {}}Есть еще некоторая оставшаяся часть этого модульного теста:
Это («не затрагивает исключения с ошибками», function () {var fn = function () {throw new TypeError ('error');}; Faillpromise (function () {target (fn);}). должен. be.rejectedwith });}Я должен закончить этот обработчик обещанием проверить исключение. Обратите внимание, что, хотя мой код все в попытке ... поймать, все еще появляется целостное исключение. Да, попробуйте ... поймать только работает в отдельной среде исполнения. Когда исключение брошено, среда выполнения интерпретатора больше не является текущим блоком Try-Catch. Такое поведение происходит аналогично вызовам Ajax. Итак, теперь есть два варианта. Альтернатива - поймать исключения в асинхронном обратном вызове:
setTimeout (function () {try {fn ();} catch (e) {// обрабатывать эту асинхронную ошибку}}, 1);Хотя этот метод полезен, есть еще много возможностей для улучшения. Во -первых, попробуйте ... Кодовые блоки Catch появляются везде в коде. На самом деле, в 1970 -х годах вызовы программирования они хотели, чтобы их код отступился. Кроме того, двигатель V8 препятствует использованию блоков кода Try… Catch в функциях (V8 - это двигатель JavaScript, используемый Chrome Browser и Node). Они рекомендуют написать эти блоки кода, которые затрагивают исключения в верхней части стека вызовов.
Итак, что это говорит нам? Как я уже говорил выше, необходимы обработчики глобальных ошибок в любом контексте выполнения. Если вы добавите обработчик ошибок в окно -объект, то есть все готово! Разве не приятно следовать принципам сухого и твердого? Глобальный обработчик ошибок сохранит ваш код читаемым и чистым.
Ниже приведен отчет, напечатанный при обработке исключений на стороне сервера. Обратите внимание, что если вы используете код в примере, выход может немного различаться в зависимости от используемого вами браузера.
Этот обработчик может даже сказать мне, какая ошибка исходит из асинхронного кода. Это говорит мне, что ошибка исходит от обработчика SetTimeout (). Так круто!
Ошибки являются частью каждого приложения, но правильная обработка ошибок не является. Есть как минимум два способа справиться с ошибками. Одним из них является решение неудачи, молчания, то есть игнорировать ошибки в коде. Другим способом является быстрое обнаружение и разрешение ошибок, то есть остановиться при ошибке и воспроизводить. Я думаю, что я четко выразил то, что мне нравится и почему мне это нравится. Мой выбор: не скрывайте проблему. Никто не винит вас в неожиданных событиях в вашей программе. Это приемлемо, чтобы сломать точки, воспроизводить и попробовать пользователя. В несовершенном мире важно дать себе шанс. Ошибки неизбежны, и то, что вы делаете, важно для решения ошибки. Разумное использование функций обработки ошибок JavaScript и автоматического и гибкого декодирования может сделать опыт пользователя более плавным, а также облегчить работу диагноза разработчика.