Javascript.RU

Устойчивость системы обработчиков к ошибкам

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

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

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

Можно завернуть вызов каждого обработчика в try .. catch. Тогда исключение будет поймано.

Что делать дальше?

Есть варианты.. Например, библиотека Yahoo UI хранит последнее пойманное исключение в специальной переменной lastError. А фреймворк Mochikit собирает все пойманные исключения в errors.

В коде ниже проиллюстрирован подход из Mochikit:

function commonHandle(event) {
  event = fixEvent(event)
  handlers = this.events[event.type]
  
  // (1)
  var errors = []
 
  for ( var g in handlers ) {
    try {
      var ret =  handlers[g].call(this, event)
      if ( ret === false ) {
        event.preventDefault()
        event.stopPropagation()
      }
    } catch(e) {
      // (2)
      errors.push(e)
    }
  }
  
  // (3)
  if (errors.length == 1) {
      throw errors[0]
  } else if (errors.length > 1) {
      var e = new Error("Multiple errors thrown in handling 'sig', see errors property");
      e.errors = errors
      throw e
  }  
}
  1. Инициализуется массив errors для исключений
  2. Все исключения - добавляем в массив, при этом не прерывая цепочку обработчиков.
  3. Если была только одна ошибка - кидаем ее дальше. Иначе делаем общий объект Error со списком ошибок в свойстве errors и кидаем его.

Каждый вызов обработчика можно завернуть в setTimeout.
Пожалуй, это самый простой способ.

// вместо
handlers[g].call(this, event)
// поставить
setTimeout(function() { handlers[g].call(..) }, 0)

При этом - да, обработчики будут вызваны независимо. Но к моменту, когда сработает setTimeout - событие будет уже "мертво", нельзя будет ни вызвать preventDefault, ни отменить всплытие.

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

Поэтому, увы, этот способ никак не годится.

Этот подход описан Dean Edwards в статье Callbacks and Events

Он заключается в том, что каждое событие заворачивается не в setTimeout, а в dispatchEvent (fireEvent для IE).

Таким образом, события вызываются в независимых потоках выполнения и инициируют ошибки независимо.

Есть несколько вариантов, как сделать систему событий устойчивой к ошибкам.
Жизнеспособны - первый подход (YUI/Mochikit) и последний (Dean).

Их можно с успехом использовать на практике.


Автор: tenshi, дата: 10 февраля, 2010 - 03:19
#permalink

Автор: Гость (не зарегистрирован), дата: 14 апреля, 2011 - 08:28
#permalink

Я оборачиваю вызов каждого обработчика в try, и в catch делаю setTimeout с повторной генерацией оригинальной ошибки. В результате, и выполнение обработчиков не прерывается, и ошибка будет показана родная, то есть будет показана и строка в которой возникла ошибка и прочая инфа.

// Фрагмент общего обработчика для MISE
// только для msie
        // this -- нод на котором происходит событие
        handlers = this[eventsPropertyName][event.type]
        // ... пропущена инициализация fixEvent
        // копирование недостающих свойств в event msie
        for (var propertyName in fixEvent) {
            if (!event[propertyName]) {
                event[propertyName] = fixEvent[propertyName]
            };
        };

        // для всех обработчиков повешенных на текущий нод для текущего типа события
        for (var i in handlers) {
            try {
                ret = handlers[i].call(this, event);
            } catch (err) {
                // собственно вот:
                setTimeout(
                    (function (err) {
                        return function () { throw err }
                    })(err), 0
                );
            };
            if (ret === false) {
               returnValue = false;
            };
        };
        return returnValue;

Спасибо вам за статьи. Сам бы только много позже обратил бы внимание на такой момент


Автор: tenshi, дата: 14 июля, 2011 - 21:02
#permalink

правильная строка будет показана лишь в фф, а остальные покажут куда попало.

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

.ня


Автор: Гость (не зарегистрирован), дата: 15 декабря, 2011 - 16:12
#permalink

не проще ли сразу честно вешать обработчики на события?

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


Автор: zenitchik (не зарегистрирован), дата: 13 августа, 2012 - 13:16
#permalink

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


Отправить комментарий

Приветствуются комментарии:
  • Полезные.
  • Дополняющие прочитанное.
  • Вопросы по прочитанному. Именно по прочитанному, чтобы ответ на него помог другим разобраться в предмете статьи. Другие вопросы могут быть удалены.
    Для остальных вопросов и обсуждений есть форум.
P.S. Лучшее "спасибо" - не комментарий, как все здорово, а рекомендация или ссылка на статью.
Содержание этого поля является приватным и не предназначено к показу.
  • Адреса страниц и электронной почты автоматически преобразуются в ссылки.
  • Разрешены HTML-таги: <strike> <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <u> <i> <b> <pre> <img> <abbr> <blockquote> <h1> <h2> <h3> <h4> <h5> <p> <div> <span> <sub> <sup>
  • Строки и параграфы переносятся автоматически.
  • Текстовые смайлы будут заменены на графические.

Подробнее о форматировании

CAPTCHA
Антиспам
1 + 5 =
Введите результат. Например, для 1+3, введите 4.
 
Текущий раздел
Поиск по сайту
Реклама
Содержание

Учебник javascript

Основные элементы языка

Сундучок с инструментами

Интерфейсы

Все об AJAX

Оптимизация

Разное

Дерево всех статей

Последние комментарии
Последние темы на форуме
Forum