最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - In what order do angular watchers and event listeners execute? - Stack Overflow

programmeradmin1浏览0评论

If one changes a scope property first, and then broadcasts an event second, will the corresponding watcher callback and event listeners callback always be executed in that same order? For example:

$scope.foo = 3;
$scope.$broadcast('bar');

and elsewhere:

$scope.$watch('foo', function fn1(){...});
$scope.$on('bar', function fn2(){...});

Will fn1 always be executed prior to fn2, or visa-versa, or can the order not be relied upon? Please cite sources, preferably to official angular docs.

In case it matters: lets assume the $scope.foo= and the $broadcast occur in a function invoked by an ng-click (i.e. user interaction)

[aside] Sorry question title is sloppy - please rename if you have something better.

If one changes a scope property first, and then broadcasts an event second, will the corresponding watcher callback and event listeners callback always be executed in that same order? For example:

$scope.foo = 3;
$scope.$broadcast('bar');

and elsewhere:

$scope.$watch('foo', function fn1(){...});
$scope.$on('bar', function fn2(){...});

Will fn1 always be executed prior to fn2, or visa-versa, or can the order not be relied upon? Please cite sources, preferably to official angular docs.

In case it matters: lets assume the $scope.foo= and the $broadcast occur in a function invoked by an ng-click (i.e. user interaction)

[aside] Sorry question title is sloppy - please rename if you have something better.

Share Improve this question edited Dec 29, 2015 at 21:17 tom asked Dec 29, 2015 at 21:02 tomtom 2,2992 gold badges17 silver badges28 bronze badges 5
  • 1 Nothing official but I'm pretty sure that's all handled asynchronously so you cannot be guaranteed of any order. Race condition. – ryanyuyu Commented Dec 29, 2015 at 21:33
  • @ryanyuyu thanks, that's my suspicion too. If the broadcast is called in a $timeout(fn, 0), can we be assured that the callback for the $on executes after then $watch callback? – tom Commented Dec 29, 2015 at 21:58
  • I'm not sure there. It's possible that just lessens the effects of the race condition. Better to just chain promises. Or other callbacks. – ryanyuyu Commented Dec 29, 2015 at 22:44
  • 1 right, a lessened race condition just makes it harder to find! Maybe I'll ask a separate question about that. – tom Commented Dec 29, 2015 at 22:51
  • See my updated answer below. I am fairly certain the event handler gets called before the watch function. – Shaun Scovil Commented Dec 29, 2015 at 23:38
Add a ment  | 

1 Answer 1

Reset to default 6

To understand what is happening, you need to understand Angular's $digest cycle and event $emit and $broadcast functions.

Based on some research, I've also learned that Angular does not use any kind of polling mechanism to periodically check for model changes. This is not explained in the Angular docs, but can be tested (see this answer to a similar question).

Putting all of that together, I wrote a simple experiment and concluded that you can rely on your event handlers running first, then your watch functions. Which makes sense, because the watch functions can be called several times in succession during the digest loop.

The following code...

template.html

<div ng-app="myApp">
  <div watch-foo ng-controller="FooController">
    <button ng-click="changeFoo()">
      Change
    </button>
  </div>
</div>

script.js

angular.module('myApp', [])
  .directive('watchFoo', watchFooDirective)
  .controller('FooController', FooController);

function watchFooDirective($rootScope) {
  return function postLink(scope) {
    scope.$watch(function () {
        return scope.foo;
    }, function (value) {
        console.log('scope.$watch A');
    });
    scope.$on('foo', function (value) {
        console.log('scope.$on A');
    });
    $rootScope.$on('foo', function (value) {
        console.log('$rootScope.$on A');
    });
    $rootScope.$on('foo', function (value) {
        console.log('$rootScope.$on B');
    });
    scope.$on('foo', function (value) {
        console.log('scope.$on B');
    });
    scope.$watch(function () {
        return scope.foo;
    }, function (value) {
        console.log('scope.$watch B');
    });
  };
}

function FooController($scope) {
  $scope.foo = 'foo';
  $scope.changeFoo = function() {
    $scope.foo = 'bar';
    $scope.$emit('foo');
  };
}

...yields the following results in the console when the 'Change' button is clicked:

scope.$on A
scope.$on B
$rootScope.$on A
$rootScope.$on B
scope.$watch A
scope.$watch B

UPDATE

Here is another test that illustrates the watch callback being called twice in the digest loop, but the event handlers not being called a second time: https://jsfiddle/sscovil/ucb17tLa/

And a third test that emits an event inside the watch function, then updates the value being watched: https://jsfiddle/sscovil/sx01zv3v/

In all cases, you can rely on the event listeners being called before the watch functions.

发布评论

评论列表(0)

  1. 暂无评论