使用Angular的$ interval服务而不触发摘要

时间:2016-01-12 11:24:35

标签: javascript angularjs

我在模板中有一个计时器,它使用angular的$ interval服务每秒更新一次。

        var now = moment();
        var startActual = moment(scope.vm.start_actual);
        var duration = now.diff(startActual);
        timer = $interval(function(){
          duration = moment.duration(duration + 1000, 'milliseconds');
          var d = Math.abs(duration.days());
          var h = Math.abs(duration.hours());
          var m = Math.abs(duration.minutes());
          var s = Math.abs(duration.seconds());
          scope.mins = prefix + (d <= 9 ? "0"+d : d) + ':' + (h <= 9 ? "0"+h : h) + ':' + (m <= 9 ? "0"+m : m) + ':' + (s <= 9 ? "0"+s : s);
        }, 1000);

我的观点中有{{mins}}。这工作正常,但我刚刚意识到这是每次运行此功能时触发一个摘要循环(每秒)。这个应用程序非常复杂,这似乎是一个巨大的不必要的CPU加载,当我所有更新是一个计时器。我注意到$ interval函数作为一个可选的invokeApply参数,但当我将其设置为false时,计时器根本不会渲染到我的模板。如何设置此计时器以使其每秒更新一次,但不会触发完整的摘要周期?任何帮助将不胜感激!

3 个答案:

答案 0 :(得分:4)

$interval有一个额外的fourth argument可用于此:

timer = $interval(function(){
  // Your code here
}, 1000, 0, false);

在这个例子中:

  • 1000是您的间隔
  • 0是重复次数(0表示无限制)
  • false表示不执行脏检查(不要使用'apply'进行更改)

现在,如果您需要其他观察者意识到您已更新mins变量,那么您肯定需要触发摘要周期。

由于您希望避免这种情况,因此只有将invokeApply设置为false并在这种情况下直接操作DOM才能实现最佳性能。

答案 1 :(得分:2)

对于这样一个非常小的一点,我建议使用小指令直接更新元素而不会每秒导致$digest循环(当你有更大的应用程序时,这可能真的很麻烦。

我很快写了这样的指令

http://plnkr.co/edit/oA0C6ou29hlKZYVhkRNv?p=preview

app.directive('procrastination', function() {
  return {
    link: function(scope, element) {
      var time;
      time = 0;
      setInterval(function() {
        time++;
        element.text("You have wasted " + time + "s");
      }, 1000);
    }
  };
});

答案 2 :(得分:0)

Angular仅在摘要中执行更新,因此如果您将invokeApply $interval参数设置为false,您的视图将不会自动更新,正如您已经注意到的那样。

但是,摘要可能不像您想象的那样占用CPU密集度。 Angular的摘要工作方式是它遍历所有范围并检查已注册的手表(内部角度特征,如模板插值也添加手表),将当前值与先前值进行比较。只有当观察到的表达式发生变化时才会进行更新。如果唯一改变的是一个变量,摘要可能只需要几毫秒,即使对于复杂的应用程序也是如此。你可以为自己计时。

var start = +new Date();
$rootScope.$digest();
console.log(+new Date() - start);

如果速度太慢,您只能触发特定范围的摘要,而且它是孩子。在你的情况下

timer = $interval(function() {
    ...
    scope.$digest();
}, 1000, 0, false);

只会对scope和任何子范围进行摘要,而不是完整的摘要。如果您的CPU密集度最高的手表不在此范围内,这将更快。

请记住,这仅适用于scope.$digestscope.$apply触发完整摘要(来自$rootScope)。