Вы знаете, как это бывает: большой проект долго проектируют, долго пишут, иногда мучаются и в конце концов сдаются.
Проходит месяц очередной «горячей отладки», а потом наступает благоговейное молчание.
Мы ничего не услышали от клиента.
И не потому, что он разорился благодаря вашим усилиям; Счета за телефон у него не оплачены, а интернет давно отключен, нет) У него все работает как обычно.
Но однажды.
Верно! Мыло прибыло» ваша программа не работает "(с)баш), телефоны раскаляются докрасна, а юристы нервно перечитывают то, что положили в раздел "гарантийное обслуживание".
У нас была точно такая же ситуация.
Мы делали довольно весомый проект, суть которого можно было описать так (вкратце, конечно): есть разный контент (клиентская база, маркетинговая база, база данных подключений и т. д., т. п.
и т. п.
) и разные способы его представления (виджет, всплывающее окно, модальное окно и т. д.).
То есть с нашей стороны была подготовлена платформа (API доступа к данным, визуализация, вся экосистема, как модно говорить (хотя я не знаю, что это значит, но звучит очень круто)) для того, чтобы разработчики заказчика могли записать данные своих контроллеров и просто «поместить» их в файл в указанное место, после чего вы с радостью увидите, как появляется новенький виджет со списком текущих котировок по какому-то хитроумному индексу.
И, как я уже сказал, все шло хорошо.
Мы провели несколько «мастер-классов», все показали, все рассказали, выпили пива и начали веселиться.
Уже без нас.
Пока все не сломалось.
Правильно: «все» и «сломалось».
В какой-то момент приложение просто начало зависать.
Настолько, что закрыть вкладку браузера невозможно.
Более-менее опытный веб-разработчик сразу скажет, что у вас где-то застрял шлейф, ребята.
И он будет прав, и что.
Но прежде чем перейти к этим 42 строкам кода, я просто напомню, что когда у вас есть приложение с кучей всяких вкусностей внутри, то лучший способ организовать связь между ними — через внутренние события.
А у наших на момент сдачи проекта их было около сотни, а когда начались проблемы, их число увеличилось еще на пару десятков.
И, как вы уже догадались, застрял именно контроллер событий.
На пальцах: событие А вызывает событие Б, а событие Б вызывает событие С, а оно, в свою очередь, снова вызывает событие А.
Та-да-м, встречайте цикл! Наш обработчик событий был невероятно простым и содержался в файле из 44 строк кода.
Однако он не знал, как сделать очень важную вещь — проверить, не попал ли он в петлю.
Мне не пришлось много думать о том, чтобы выпить, и решение было найдено довольно быстро.
Хорошо это или плохо, зависит только от вас.
Опишу только основную идею.
Единственный способ проверить, «кто» стал причиной цепочки событий (в нашем примере — нахождение A, B и C) — это проверить стек.
Чтобы получить стек нужно просто «кинуть» ошибку.
Остается проблема — как «пометить» место вызова события, ведь в стеке должно быть что-то, что помогло бы распознать всю цепочку ранее запущенных событий? Одним из решений этой проблемы являются именованные функции-обертки, в имени которых хранится вся информация о предыдущих событиях.
Ничего не понимаю? Написал, перечитал и тоже не понял.
Легче смотреть код .
По сути, теперь, если вы сделаете это (событие A вызывает B, B вызывает C, а C снова вызывает A):
На этот раз приложение не уйдет в подвешенное состояние, а выдаст исключение «Неперехваченная ошибка: событие [A] вызвало само себя.var safeevents = new SafeEvents(); safeevents.bind('A', function () { safeevents.trigger('B'); }); safeevents.bind('B', function () { safeevents.trigger('C'); }); safeevents.bind('C', function () { safeevents.trigger('A'); }); safeevents.trigger('A');
Полная цепочка: А, Б, С».
Прибыль.
Теперь разработчику не нужно делать три лишних перекура, чтобы разобраться, что происходит на самом деле — все видно из сообщения в консоли.
Асинхронные вызовы немного сложнее.
Для них, к сожалению, нужно выполнить дополнительное действие.
Но он настолько мал, что вряд ли станет большой проблемой.
var safeevents = new SafeEvents();
safeevents.bind('A', function () {
safeevents.trigger('B');
});
safeevents.bind('B', function () {
safeevents.trigger('C');
});
safeevents.bind('C', function () {
/*
* Use method "safely" to wrap your async methods and create safe callback.
*/
setTimeout(safeevents.safely(function () {
safeevents.trigger('A');
}), 10);
});
safeevents.trigger('A');
Обратите внимание на функцию обратного вызова в таймере.
Добавляем «обертку» для передачи данных о предыдущих событиях в асинхронных вызовах.
И снова в консоли увидим: «Uncaught Error: Event [A] вызвало само себя.
Полная цепочка: А, Б, С».
Конечно, не всегда нужно делать это и нагло выбрасывать исключение.
Гораздо лучше незаметно сообщить, куда следует записывать данные в журнал или отправить уведомление админу.
Почему можно поставить свой обработчик на случай «зацикливания» и получить все необходимые данные.
var safeevents = new SafeEvents();
safeevents.bind('A', function () {
safeevents.trigger('B');
});
safeevents.bind('B', function () {
safeevents.trigger('C');
});
safeevents.bind('C', function () {
safeevents.trigger('A');
});
safeevents.bind(safeevents.onloop, function (e, chain, last_event, stack) {
console.log('Error message: ' + e);
console.log('Full chain of events: ' + chain.join(', '));
console.log('Last event (generated loop): ' + last_event);
console.log('Error stack: ' + stack);
});
safeevents.trigger('A');
Теперь наше приложение вообще не будет прерываться, но цикл будет успешно предотвращен.
В общем, переписав наш простейший обработчик событий и получив дополнительно 42 строки кода, мы решили очень редкую, но довольно неприятную проблему с зацикливанием событий.
Кто знает (не знаю), возможно, и вам это будет полезно.
Все здесь .
Счастья, добра и электричества в ваши дома.
Теги: #JavaScript #node.js #events #цикл событий #emmiter #Разработка веб-сайтов #JavaScript #node.js
-
Должны Ли Android И Symbian Объединиться?
19 Oct, 24 -
Компьютерра Закрывается
19 Oct, 24 -
Мы Совсем Сошли С Ума
19 Oct, 24