【问题标题】:angularJS $on event handler trigger orderangularJS $on 事件处理程序触发顺序
【发布时间】:2013-07-03 14:56:30
【问题描述】:

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

  1. 如何定义监听同一事件的处理程序的触发顺序?
  2. 如果您开始对此感到疑惑,是否表明设计不佳?

在阅读了有关 angular $on, $broadcast and $emit 和原生 DOM event flow 的文档后,我想我明白事件处理程序将在不同范围内触发的顺序。问题是当多个处理程序从不同的地方(例如控制器与服务)在同一范围(例如 $rootScope)中侦听时。

为了说明问题,我将一个带有一个控制器和两个服务的 jsfiddle 放在一起,所有这些都通过 $rootScope http://jsfiddle.net/Z84tX/ 进行通信

Thanks

【问题讨论】:

    标签: angularjs event-handling broadcast


    【解决方案1】:

    很好的问题。

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

    我之前并没有真正考虑过这一点,因为我的处理程序从不需要知道哪个先运行,但是从你小提琴的外观来看,我可以看到处理程序的调用顺序与它们初始化的顺序相同。

    在你的小提琴中,你有一个控制器 controllerA,它依赖于两个服务,ServiceAServiceB

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

    服务和控制器都定义了一个事件监听器。

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

    然后,您可能还会观察到服务按照注入的顺序进行了初始化。所以ServiceAServiceB 之前被初始化,因为它们按顺序注入到控制器中。如果您在控制器签名中更改了它们的顺序,您会看到它们的初始化顺序也发生了变化(ServiceB 位于 ServiceA 之前)。

    因此,在服务初始化后,控制器也会初始化,其中定义的事件处理程序也会随之初始化。

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

    【讨论】:

      【解决方案2】:

      走这条路有点乱(我不推荐),但想提供一个替代方案,以防您无法确保 serviceB 将在 serviceA 之前初始化,并且您绝对需要先执行 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);
      

      【讨论】:

      • 即使这是一个肮脏的 hack,有时也没有其他方法可以更改监听器的顺序,尤其是当另一个监听器是由第三方代码添加时。
      • 不错。如果可以在附加侦听器时指定某种优先级,那就更好了。
      • 等效:listenersArray.unshift(listenersArray.splice(-1, 1)[0]);
      • 或者更简单:listenersArray.unshift(listenersArray.pop());
      【解决方案3】:

      提供#2 的答案(因为我认为@Stewie 对#1 的答案非常好),而我会犹豫是否提供结论性规则,说:“如果你看到这个,那么这是糟糕的代码",我提出,如果你有两个事件处理程序,一个只能在另一个运行后执行:你应该评估为什么会这样,如果你不能更好地封装或组织你的逻辑执行方式。

      发布/订阅事件广播/监听的主要用例之一是允许完全独立的独立组件以独立的方式在其影响域上异步操作。通过一个处理程序必须在另一个处理程序首先运行后才进行操作,您通过添加次要要求(尽管可能是必要的)来消除 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”函数,该函数还定义了侦听器的排序方式。然后,您可以将此服务注入到需要触发事件的任何其他组件中。

        【讨论】:

          【解决方案6】:

          它有点 hacky,绝对不建议在你的设计中使用,但有时你别无选择(去过很多次)。

          $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 延迟。

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2014-11-09
            • 2012-04-15
            • 2011-09-07
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-11-23
            相关资源
            最近更新 更多