angularJS $on event handler trigger order

折月煮酒 提交于 2019-12-02 19:00:26

Very good question.

Event handlers are executed in order of initialization.

I haven't really thought about this before, because my handlers never needed to know which one run first, but by the look of you fiddle I can see that the handlers are called in the same order in which they are initialized.

In you fiddle you have a controller controllerA which depends on two services, ServiceA and ServiceB:

myModule
  .controller('ControllerA', 
    [
      '$scope', 
      '$rootScope', 
      'ServiceA', 
      'ServiceB', 
      function($scope, $rootScope, ServiceA, ServiceB) {...}
    ]
  );

Both services and the controller define an event listener.

Now, all dependencies need to be resolved before being injected, which means that both services will be initialized before being injected into the controller. Thus, handlers defined in the services will be called first, because service factories are initialized before controller.

Then, you may also observe that the services are initialized in order they are injected. So ServiceA is initialized before ServiceB because they are injected in that order into the controller. If you changed their order inside the controller signature you'll see that their initilization order is also changed (ServiceB comes before ServiceA).

So, after the services are initialized, the controller gets initialized as well, and with it, the event handler defined within.

So, the end result is, on $broadcast, the handlers will be executed in this order: ServiceA handler, ServiceB handler, ControllerA handler.

It's a bit messy (and I wouldn't recommend it) to go about this route, but wanted to provide an alternative incase you are unable to ensure serviceB will be initialized before serviceA and you absolutely need serviceB's listener to be executed first.

You can manipulate the $rootScope.$$listeners array to put serviceB's listener first.

Something like this would work when adding the listener to $rootScope on 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);

To provide an answer to #2 (because I think @Stewie's answer to #1 is a really good one), while I'd hesitate to ever offer conclusive rules that say, "if you see this, then it's bad code", I would offer to say that if you have two event handlers, and one can only execute after the other has run: you should evaluate why that is the case and if you couldn't better encapsulate or organize the way your logic executes.

One of the primary use cases of pub/sub event broadcasting/listening is to allow separate components that are fully independent of one another to operate on their domain of influence in an independent way asynchronously. By one handler having to operate only after another handler has run first you are removing the asynchronous nature of pub/sub by adding a secondary requirement (though possibly necessary).

If it's an absolutely necessary dependency, then no: it's not a symptom of bad design - its a symptom of the requirements of that feature.

I'm a new user to Angular JS so please forgive if the answer is less than optimal. :P

If you require the order of the functions triggered by an event to be dependent (i.e., Function A then Function B) then might not creating a trigger function be better?

function trigger(event,data) {
    FunctionA(event,data);
    FunctionB(event,data);
}

$rootScope.on('eventTrigger',trigger);

To add another comment on point #2, if you need to guarantee order you could implement an observer pattern using a service with an array of listeners. In the service you can define "addListener" functions that also defines how the listeners are ordered. You can then inject this service into whatever other components need to fire events.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!