18

AngularJS でサードパーティ ツールや外部の DOM イベントを処理する際に留意すべき主な点の 1 つは、$scope.$apply()メソッド操作を使用して変更を開始することです。これは驚くほど機能しますが、スコープ自体がすでにダイジェストをすりつぶしていて (これは基本的に $apply メソッドがトリガーするものです)、これが行われているときに $apply を呼び出すとエラーがスローされることがあります。$scope.$$phaseしたがって、これを回避するには、ダイジェストが行われるたびにスコープに設定されるフラグに注意を払う必要があります。

ここで、URL を変更したいとします。

$scope.$apply(function() {
  $location.path('/home');
});

これは期待どおりに機能しますが、ここで $scope がビジー状態であると仮定しましょう。そのため、代わりに $$phase 変数を確認し、変更が反映されると想定します。

if($scope.$$phase) {
  $location.path('/home');
}
else {
  $scope.$apply(function() {
    $location.path('/home');
  });
}

これは私が行ってきたことであり (明らかにコードの重複ではありません)、100% の時間で動作するようです。私が懸念しているのは、スコープが消化の途中にあるときに、AngularJS がどのように変更を取得するかということです。

おそらく、この例は十分に具体的ではありません。代わりにもっと大きなものを想定しましょう。大量のバインディングがあり、消化がページを直線的に消化すると仮定できる巨大な Web ページがある場合を想像してみてください (優先順位に関してこのようなことを行うと仮定しています ... この場合、最初に DOM ツリー)、ページのバインディングを上から下に更新します。

<div class="binding">{{ binding1 }}</div>
<div class="binding">{{ binding2 }}</div>
<div class="binding">{{ binding3 }}</div>
<div class="binding">{{ binding4 }}</div>
<div class="binding">{{ binding5 }}</div>
<div class="binding">{{ binding6 }}</div>
<div class="binding">{{ binding7 }}</div>
<div class="binding">{{ binding8 }}</div>

消化が進行中で、それが消化キューのほぼ中央にあると仮定しましょう。ここで、ページの上部にあるバインディングの値をどこかで変更してみましょう。

if($scope.$$phase) {
  $scope.binding1 = 'henry';
}

どういうわけか、AngularJS は変更を取得し、バインディングを適切に更新します。変更自体は、HTML/DOM に関してキューの早い段階で行われると見なすことができますが。

私の質問は、AngularJS がこの潜在的な競合状態をどのように管理するかということです。binding8が更新された場合 (ページの下部にあるため)は多少快適ですが、 binding1も更新されるため ($apply を再度呼び出す必要なしにすぐに)、少し迷ってしまいます。これは、別の消化がその間のどこかに派遣されたことを意味しますか? それとも、$scope オブジェクトは私が予想するよりも魔法のようなものですか? この問題は以前からあったと思いますが、そもそも $$phase と $scope を見つけるのが少し難しいので、これも見過ごされた可能性があると思います。

何か案は?

4

3 に答える 3

19

バインディングとレース コンディションについて。$digestは、変更がなくなるまですべてのウォッチャーをループします。ログをウォッチャー/バインドされたメソッドに追加することで確認できるように、各バインディング/ウォッチャーを少なくとも 2 回呼び出して、変更がなく、すべてのバインドされた値が安定していることを確認します。これらは、すべての値が解決されるまで実行される (2 回のループ反復内で変更されていない) ダーティ チェックです。それが役立つことを願っています。

これについては、こちらの AngularJS ドキュメントで説明されています: http://docs.angularjs.org/api/ng.$ro ​​otScope.Scope#$digest

注: これは、matsko から要求された私のコメントのコピー/貼り付けです。

于 2012-11-30T16:19:15.107 に答える
1

Applydigest何も変更されていないことを確認するまで呼び出しを続けます。そのため、最初の呼び出しで競合状態が発生する可能性がありますが、2 回目の呼び出しでは常に補償されます。

于 2012-11-30T15:14:35.483 に答える
0

とにかく、その回避策を使用しないでください。$scopeこの関数の状態が不明な場合は、角度によって同期的に処理されているかどうかがわかっているコールスタックの上位で消化する必要があるため、消化する必要はありません。モデルを非同期に変更しているため、モデルを消化できます。

ちなみに、$scope.$applyは基本的にエラー処理付きです。そのため、ディレクティブ内で分離されたスコープを更新する場合は、代わりに を$scope.$root.$digest呼び出すことでパフォーマンスを節約できます。$digest$apply

// Somewhere in a callback
$location.path('/home');
$scope.$digest();

PS: これは、e-satis の回答を修正するため$digestのものです。スコープがダーティな間は、それ自体も呼び出されます。

于 2014-04-10T22:41:32.280 に答える