事件处理程序触发顺序上的angularJS$

事件处理程序触发顺序上的angularJS$,angularjs,event-handling,broadcast,Angularjs,Event Handling,Broadcast,在angularJS事件处理的上下文中,我想知道两件事 如何定义侦听同一事件的处理程序被触发的顺序 如果你开始怀疑这一点,这是一个糟糕设计的标志吗 在阅读了angular和native的文档之后,我想我理解了事件处理程序在不同范围内的触发顺序。问题是当多个处理程序从不同的地方(例如控制器和服务)在同一范围内侦听时(例如,rootScope) 为了说明这个问题,我将一个JSFIDLE与一个控制器和两个服务组合在一起,所有这些都通过$rootScope进行通信 谢谢问得好 事件处理程序按初始化顺序执

在angularJS事件处理的上下文中,我想知道两件事

  • 如何定义侦听同一事件的处理程序被触发的顺序
  • 如果你开始怀疑这一点,这是一个糟糕设计的标志吗
  • 在阅读了angular和native的文档之后,我想我理解了事件处理程序在不同范围内的触发顺序。问题是当多个处理程序从不同的地方(例如控制器和服务)在同一范围内侦听时(例如,rootScope)

    为了说明这个问题,我将一个JSFIDLE与一个控制器和两个服务组合在一起,所有这些都通过$rootScope进行通信

    谢谢

    问得好

    事件处理程序按初始化顺序执行。

    我以前没有真正考虑过这一点,因为我的处理程序从来都不需要知道先运行哪一个,但是通过查看fiddle,我可以看到处理程序的调用顺序与它们初始化的顺序相同

    小提琴中有一个控制器
    controllerA
    ,它依赖于两种服务,
    ServiceA
    ServiceB

    myModule
    .controller('ControllerA',
    [
    “$scope”,
    “$rootScope”,
    “服务A”,
    “服务B”,
    函数($scope,$rootScope,ServiceA,ServiceB){…}
    ]
    );
    
    服务和控制器都定义了一个事件侦听器

    现在,所有依赖项都需要在注入之前解析,这意味着这两个服务都将在注入控制器之前初始化。因此,将首先调用服务中定义的处理程序,因为服务工厂是在控制器之前初始化的

    然后,您还可以观察到服务是按注入顺序初始化的。因此,
    ServiceA
    ServiceB
    之前初始化,因为它们是按该顺序注入控制器的。如果您在控制器签名中更改了它们的顺序,您将看到它们的初始化顺序也发生了更改(
    ServiceB
    位于
    ServiceA
    之前)

    因此,在初始化服务之后,控制器也会被初始化,并使用它来初始化中定义的事件处理程序


    因此,最终结果是,在$broadcast上,处理程序将按以下顺序执行:
    ServiceA
    handler,
    ServiceB
    handler,
    ControllerA
    handler。

    我是Angular JS的新用户,因此如果答案不是最佳答案,请原谅:P

    如果您要求事件触发的函数的顺序是依赖的(即,函数A然后是函数B),那么不创建触发函数可能更好吗

    function trigger(event,data) {
        FunctionA(event,data);
        FunctionB(event,data);
    }
    
    $rootScope.on('eventTrigger',trigger);
    
    这条路线有点混乱(我不推荐这样做),但我想提供一个替代方案,以防您无法确保在serviceA之前初始化serviceB,并且您绝对需要首先执行serviceB的侦听器

    您可以操纵
    $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提供一个答案(因为我认为@Studie对#1的答案真的很好),虽然我不愿意给出结论性的规则,说“如果你看到这个,那么它就是坏代码”,但我想说,如果你有两个事件处理程序,其中一个只能在另一个运行之后执行:您应该评估为什么会出现这种情况,以及是否无法更好地封装或组织逻辑执行的方式

    发布/订阅事件广播/收听的一个主要用例是允许彼此完全独立的独立组件在其影响范围内以独立方式异步运行。由于一个处理程序必须在另一个处理程序先运行后才运行,因此通过添加第二个需求(尽管可能是必要的),您正在删除发布/订阅的异步特性


    如果这是一个绝对必要的依赖关系,那么答案是否定的:这不是糟糕设计的症状——这是该功能需求的症状。

    要对第2点添加另一个注释,如果您需要保证顺序,您可以使用带有侦听器数组的服务实现观察者模式。在服务中,您可以定义“addListener”函数,该函数还定义侦听器的排序方式。然后,您可以将此服务注入任何其他需要触发事件的组件中。

    这有点粗糙,绝对不建议在您的设计中使用,但有时您没有选择余地(已经出现过很多次)

    $rootScope.$on('someEvent',()=>{
    设置超时(eventHandler1,1);
    });
    $rootScope.$on('someEvent',eventHandler2);
    常量eventHandler1=()=>{
    log('这个最后运行');
    };
    const eventHandler2=()=>{
    log('这个先运行');
    };
    
    正如您从示例中看到的,我使用了
    setTimeout
    来欺骗实际处理程序的运行顺序,并使
    eventHandler1
    处理程序最后运行,尽管它是先调用的

    要设置执行优先级,只需根据需要更改
    setTimeout
    延迟


    这并不理想,只适用于特定情况。

    即使这是一个肮脏的黑客行为,有时也没有其他方法来更改侦听器顺序,特别是当另一个侦听器由第三方代码添加时。很好。如果在附加侦听器时可以指定某种优先级,那就更好了。等价地:
    listenersArray.unshift(listenersArray.splice(-1,1)[0])甚至更简单:
    listenersArray.unshift(listenersArray.pop())如何触发事件?