AngularJS $на порядок запуска обработчика событий
Мне было интересно две вещи, в контексте обработки событий angularJS.
- Как определяется порядок, в котором запускаются обработчики, прослушивающие одно и то же событие?
- Является ли это признаком плохого дизайна, если вы начинаете задумываться об этом?
После чтения документации по angular $on, $broadcast и $emit, а также встроенного потока событий DOM Думаю, я понимаю, в каком порядке обработчики событий будут запускаться в разных областях. Проблема заключается в том, что несколько обработчиков прослушиваются в одной области (например, $rootScope) из разных мест (например, Controllers vs Services).
Чтобы проиллюстрировать проблему, я собрал jsfiddle с одним контроллером и двумя службами, все из которых передаются через $rootScope http://jsfiddle.net/Z84tX/
Thanks
Ответы
Ответ 1
Очень хороший вопрос.
Обработчики событий выполняются в порядке инициализации.
Я не думал об этом раньше, потому что мои обработчики никогда не нуждались в том, чтобы знать, какой из них запускается первым, но по внешности вы видите, что обработчики вызываются в том же порядке, в котором они инициализируются.
У вас есть контроллер controllerA
, который зависит от двух сервисов, ServiceA
и ServiceB
:
myModule
.controller('ControllerA',
[
'$scope',
'$rootScope',
'ServiceA',
'ServiceB',
function($scope, $rootScope, ServiceA, ServiceB) {...}
]
);
Обе службы и контроллер определяют прослушиватель событий.
Теперь все зависимости должны быть разрешены перед введением, а это означает, что обе службы будут инициализированы перед вводом в контроллер. Таким образом, обработчики, определенные в службах, будут вызваны первыми, поскольку сервисные службы инициализируются перед контроллером.
Затем вы также можете заметить, что службы инициализируются, чтобы они были введены. Поэтому ServiceA
инициализируется до ServiceB
, потому что они вводятся в этом порядке в контроллер. Если вы изменили свой порядок внутри сигнатуры контроллера, вы увидите, что их порядок инициализации также изменен (ServiceB
предшествует ServiceA
).
Итак, после инициализации сервисов контроллер также инициализируется, а вместе с ним обработчик событий определяется внутри.
Итак, конечный результат: в $broadcast обработчики будут выполняться в следующем порядке: ServiceA
обработчик, обработчик ServiceB
, обработчик controllerA
.
Ответ 2
Это немного беспорядочно (и я бы не рекомендовал его), чтобы идти по этому маршруту, но хотел предоставить альтернативный вариант, когда вы не можете гарантировать, что serviceB будет инициализирован до службы A, и вам абсолютно необходимо, чтобы прослушиватель serviceB выполнялся первым.
Вы можете манипулировать массивом $rootScope.$$listeners
, чтобы сначала включить прослушиватель serviceB.
Что-то вроде этого будет работать при добавлении слушателя к $rootScope
в serviceB:
var listener, listenersArray;
$rootScope.$on('$stateChangeStart', someFunction);
listenersArray = $rootScope.$$listeners.$stateChangeStart;
listener = listenersArray[listenersArray.length - 1];
listenersArray.splice(listenersArray.length - 1, 1);
listenersArray.unshift(listener);
Ответ 3
Чтобы дать ответ на # 2 (потому что я думаю, что ответ @Stewie на # 1 действительно хороший), в то время как я бы колебался когда-либо предлагать убедительные правила, которые говорят: "Если вы видите это, то это плохой код", Я бы сказал, что если у вас есть два обработчика событий, и можно выполнить только после запуска другого: вы должны оценить, почему это так, и если вы не можете лучше инкапсулировать или организовать способ выполнения вашей логики.
Одним из основных случаев использования вещания/прослушивания pub/sub event является предоставление отдельным компонентам, которые полностью независимы друг от друга для асинхронного использования в своей области влияния независимым образом. Посредством одного обработчика, который должен работать только после первого запуска другого обработчика, вы удаляете асинхронный характер pub/sub, добавляя дополнительное требование (хотя, возможно, необходимо).
Если это абсолютно необходимая зависимость, то нет: это не симптом плохого дизайна - это признак требований этой функции.
Ответ 4
Я новый пользователь для Angular JS, поэтому, пожалуйста, простите, если ответ менее оптимальный.: P
Если вам требуется порядок функций, инициируемых зависящим от события (т.е. функция A, затем функция B), то может быть лучше не создавать функцию триггера?
function trigger(event,data) {
FunctionA(event,data);
FunctionB(event,data);
}
$rootScope.on('eventTrigger',trigger);
Ответ 5
Чтобы добавить еще один комментарий к точке # 2, если вам нужно гарантировать заказ, вы можете реализовать шаблон наблюдателя, используя службу с массивом слушателей. В сервисе вы можете определить функции addListener, которые также определяют порядок упорядочивания слушателей. Затем вы можете применить эту услугу к другим компонентам, которые должны запускать события.