Javascript $watch未按预期的次数开火

Javascript $watch未按预期的次数开火,javascript,angularjs,watch,Javascript,Angularjs,Watch,如果您更改它正在监视的变量x次,我预计$watch将触发x次。在我放在一起的一个小例子中,我将变量$scope.foo的值更改了3次,但是关联的$watch只运行一次 <html> <head> <title></title> <script src="https://code.angularjs.org/1.3.8/angular.js"></script> </head> <

如果您更改它正在监视的变量
x
次,我预计
$watch
将触发
x
次。在我放在一起的一个小例子中,我将变量
$scope.foo的值更改了3次,但是关联的
$watch
只运行一次

<html>
  <head>
    <title></title>
    <script src="https://code.angularjs.org/1.3.8/angular.js"></script>
  </head>
  <body ng-app='myApp'>

    <div ng-controller='MyCtrl'></div>

    <script>
      var myApp = angular.module('myApp', []);

      myApp.controller('MyCtrl', function($scope) {
          $scope.$watch('foo', function(oldVal, newVal) {
              console.log(oldVal, newVal);
          });

          $scope.foo = "foo";
          $scope.foo = "bar";
          $scope.foo = "baz";
      });


    </script>
  </body>
</html>
但是

baz-baz

编辑:另一个例子
该更改对于angular来说太快,使用
$timeout
它可以工作:


var myApp=angular.module('myApp',[]);
控制器('MyCtrl',函数($scope,$timeout){
$scope.$watch('foo',函数(oldVal,newVal){
console.log(oldVal,newVal);
});
$scope.foo=“foo”;
$scope.foo=“bar”;
$scope.foo=“baz”;
$timeout(函数(){
$scope.foo=“foo”;
},0)        
$timeout(函数(){
$scope.foo=“bar”;
},0)        
$timeout(函数(){
$scope.foo=“baz”;
},0)
});

A
$watch
在每个角度$digest循环中只触发一次!(如果观看的是最简单的场景)

您对
foo
所做的三项更改都发生在同一个周期内。angular将比较循环前和循环后的值

对于您的情况,您需要触发一个新的循环,例如,更改
$timeout
中的值

编辑

举个例子,你可以这样做

myApp.controller('MyCtrl', function($scope) {
    $scope.foo = "foo";
    // run on controller init
    fooWatch();
    // watch for changes
    $scope.$watch('foo', fooWatch);

    function fooWatch() {
        $scope.bar = 'bar';
    }
});

因为
$digest
循环并不是每次更改范围中的变量时都运行的(幸运的是)

但是,当您使用
$timeout
时,它会被触发

你的用例是什么


顺便说一句,在控制器中使用
$watch
通常不是一个很好的做法,也很容易避免。

这不是太快<代码>$watch
仅在
$digest
周期内触发,并且它不会在每次更改值时运行(除非它由ngModel管理)。您在这里所做的是激发摘要3次感谢您的回答,这很有意义。我在OP中添加了另一个例子,其中有一个场景反映了我的真实用例:变量(
foo
)的更新会触发
foo
上的
$watch
中另一个变量(
bar
)的更新,但是
bar
的更新值不能立即使用。如果我包装
console.log($scope.bar)
$timeout
中,这一切都可以正常工作,但是有没有更好的方法来解决这个问题呢?为什么不在控制器中更新$scope.foo时调用想要执行的函数呢?使用
$watch
只有在您希望从视图更新中修改变量时才有意义,而不是在控制器内部手动更改变量时才有意义。我仍然不确定您在这里想要实现什么。这个示例过于简单,但我忽略的一个重要细节可能是第一个变量(
foo
)可以从视图或控制器中更新。因此,您和@Umair都建议为控制器提供一个简单的函数,并让$watch只检测视图中的更改,这是有意义的。我对$watch的主要误解是,如果由于视图中的某个操作或控制器中的某个其他更新而导致变量发生更改,那么它将是放置应该运行的代码的好地方。问题是foo是如何更新的?如果仅在控制器中更新,则无需
监视
!常规javascript在这里就可以了。如果它是通过表单输入更新的(例如),则可以使用
监视
。您还可以将
watch
函数重构为一个单独的函数调用,最初可以运行该函数来设置所有内容。请参见下面答案中的示例。
  myApp.controller('MyCtrl', function($scope, $timeout) {
      $scope.$watch('foo', function() {
          $scope.bar = 'bar';
      });

      $scope.foo = "foo";

      // now the value of $scope.foo has changed, I was expecting the $watch to have run at this point.
      // Apparently is has not because $scope.bar is still undefined. Accessing $scope.bar in a $timeout works, but is there a better way?
      console.log($scope.bar)
  });
myApp.controller('MyCtrl', function($scope) {
    $scope.foo = "foo";
    // run on controller init
    fooWatch();
    // watch for changes
    $scope.$watch('foo', fooWatch);

    function fooWatch() {
        $scope.bar = 'bar';
    }
});