在angularJS事件处理的上下文中,我想知道两件事。
阅读有关角$on, $broadcast and $emit以及原生DOM event flow的文档后,我想我了解事件处理程序将在不同范围内触发的顺序。问题是当几个处理程序从不同的地方(例如,控制器与服务)监听相同的范围(例如$ rootScope)时。
为了说明问题,我将jsfiddle与一个控制器和两个服务放在一起,所有这些都通过$ rootScope http://jsfiddle.net/Z84tX/进行通信
Thanks
答案 0 :(得分:26)
非常好的问题。
事件处理程序按初始化顺序执行。
我之前没有真正考虑过这个问题,因为我的处理程序从不需要知道哪一个先运行,但是通过你的外观,我可以看到处理程序的调用顺序与它们的初始化顺序相同。
在你的小提琴中你有一个控制器controllerA
,它取决于两个服务,ServiceA
和ServiceB
:
myModule
.controller('ControllerA',
[
'$scope',
'$rootScope',
'ServiceA',
'ServiceB',
function($scope, $rootScope, ServiceA, ServiceB) {...}
]
);
服务和控制器都定义了一个事件监听器。
现在,所有依赖项都需要在注入之前解析,这意味着两个服务都会在注入控制器之前进行初始化。因此,将首先调用服务中定义的处理程序,因为服务工厂在控制器之前初始化。
然后,您可能还会观察到服务是按照它们被注入的方式初始化的。因此ServiceA
在ServiceB
之前被初始化,因为它们按顺序注入控制器。如果您在控制器签名中更改了他们的订单,您会看到他们的初始化顺序也已更改(ServiceB
在ServiceA
之前)。
因此,在初始化服务之后,控制器也会被初始化,并且随之在其中定义事件处理程序。
因此,最终结果是,在$ broadcast上,处理程序将按以下顺序执行:ServiceA
处理程序,ServiceB
处理程序,ControllerA
处理程序。
答案 1 :(得分:3)
这条路线有点凌乱(我不建议这样做),但想提供一个替代方案,你无法确保serviceB在serviceA之前初始化,你绝对需要serviceB& #39; s侦听器首先被执行。
您可以操纵$rootScope.$$listeners
数组以首先放置serviceB的侦听器。
在serviceB上向$rootScope
添加监听器时,这样的事情会起作用:
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);
答案 2 :(得分:3)
为#2提供答案(因为我认为@ Stewie对#1的回答非常好),而我却毫不犹豫地提出确凿的规则,说“如果你看到这个,那就是糟糕的代码“,我会提出说,如果你有两个事件处理程序,并且一个只能在另一个运行之后执行:你应该评估为什么会这样,如果你不能更好地封装或组织你的逻辑执行方式。
发布/子事件广播/收听的主要用例之一是允许完全独立于彼此的独立组件以异步方式以独立方式操作。由一个处理程序必须在另一个处理程序首先运行之后才运行,你通过添加一个二级需求来删除pub / sub的异步性质(虽然可能是必要的)。
如果它是一个绝对必要的依赖,那么不:它不是设计不良的症状 - 它是该功能要求的症状。
答案 3 :(得分:1)
我是Angular JS的新用户,请原谅,如果答案不是最佳的话。 :P
如果您要求事件触发的函数的顺序依赖(即函数A然后函数B),那么可能不会更好地创建触发函数?
function trigger(event,data) {
FunctionA(event,data);
FunctionB(event,data);
}
$rootScope.on('eventTrigger',trigger);
答案 4 :(得分:0)
要在第2点添加另一条注释,如果需要保证顺序,可以使用带有一组侦听器的服务来实现观察者模式。在服务中,您可以定义" addListener"这些函数还定义了如何对侦听器进行排序。然后,您可以将此服务注入到触发事件所需的任何其他组件中。
答案 5 :(得分:0)
这有点古怪,绝对不建议在您的设计中使用它,但是有时您别无选择(去过很多次)。
$rootScope.$on('someEvent', () => {
setTimeout(eventHandler1, 1);
});
$rootScope.$on('someEvent', eventHandler2);
const eventHandler1 = () => {
console.log('This one runs last');
};
const eventHandler2 = () => {
console.log('This one runs first');
};
从示例中可以看到,我使用setTimeout
来欺骗运行实际处理程序的顺序,并使eventHandler1
处理程序最后运行,尽管它首先被调用。
要设置执行优先级,只需根据需要更改setTimeout
延迟。
这不理想,仅适用于特定情况。