web-dev-qa-db-ja.com

angularJSの$ onイベントハンドラートリガー順序

AngularJSイベント処理のコンテキストで、2つのことを考えていました。

  1. 同じイベントをリッスンするハンドラーがトリガーされる順序はどのように定義されますか?
  2. これについて考え始めたら、それは悪いデザインの兆候ですか?

angular $ on、$ broadcast and $ emit およびネイティブ DOMイベントフロー に関するドキュメントを読んだ後、どの順序で理解できると思います問題は、複数のハンドラーがさまざまな場所(コントローラーとサービスなど)から同じスコープ(たとえば$ rootScope)でリッスンする場合です。

問題を説明するために、1つのコントローラーと2つのサービスを備えたjsfiddleを作成し、すべて$ rootScopeを介して通信します http://jsfiddle.net/Z84tX/

Thanks

25
Renaud

とてもいい質問です。

イベントハンドラーは初期化の順序で実行されます。

私のハンドラーはどちらが最初に実行されるかを知る必要がないため、これについてはこれまで考えていませんでしたが、フィドルを見ると、ハンドラーは初期化されたのと同じ順序で呼び出されることがわかります。

フィドルには、controllerAServiceAの2つのサービスに依存するコントローラーServiceBがあります。

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

サービスとコントローラーの両方がイベントリスナーを定義します。

現在、すべての依存関係は、注入される前に解決する必要があります。つまり、両方のサービスがコントローラーに注入される前に初期化されます。したがって、サービスファクトリはコントローラーの前に初期化されるため、サービスで定義されたハンドラーが最初に呼び出されます。

次に、サービスが挿入されるために初期化されることも確認できます。したがって、ServiceAServiceBの前に初期化されます。これらの順序でコントローラーに挿入されるためです。コントローラシグネチャ内で順序を変更すると、初期化順序も変更されることがわかります(ServiceBServiceAの前にあります)。

したがって、サービスが初期化された後、コントローラーも初期化され、それとともに、内部で定義されたイベントハンドラーも初期化されます。

したがって、最終結果は、$ broadcastで、ハンドラーはServiceAハンドラー、ServiceBハンドラー、ControllerAハンドラーの順に実行されます。

26
Stewie

#2への答えを提供するには(#1への@Stewieの答えは本当に良いものだと思うので)、「これを見ると悪いコードだ」という決定的なルールを提供するのをためらうことはありませんが、 wouldは、2つのイベントハンドラーがあり、一方が他方が実行された後にのみ実行できる場合、その理由を評価する必要があり、ロジックの実行方法をカプセル化または整理できなかった場合。

Pub/subイベントのブロードキャスト/リスニングの主な使用例の1つは、相互に完全に独立した個別のコンポーネントが影響ドメインで動作できるようにすることです独立した方法で非同期的に。あるハンドラーが別のハンドラーが最初に実行された後にのみ動作しなければならない場合、2番目の要件を追加することにより、pub/subの非同期性を削除します(必要な場合もあります)。

絶対に必要な依存関係である場合、いいえ:それは悪い設計の症状ではありません-その機能の要件の症状です

3
Mattygabe

このルートについて説明するのは少し面倒です(お勧めしません)が、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);
3
kirkchris

私はAngular JSの新しいユーザーですので、答えが最適とは言えない場合はご容赦ください。:P

イベントによってトリガーされる関数の順序を依存させる必要がある場合(つまり、関数A、次に関数B)、トリガー関数を作成しない方が良いでしょうか?

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

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

ポイント#2に別のコメントを追加するには、順序を保証する必要がある場合、リスナーの配列を持つサービスを使用してオブザーバーパターンを実装できます。サービスでは、リスナーの順序付け方法も定義する「addListener」関数を定義できます。その後、イベントを起動するために必要な他のコンポーネントにこのサービスを注入できます。

0
GameSalutes