深入理解Angularjs 脏值检测(8)

我们再来定义$evalAsync,它添加将在这个队列上执行的函数:

Scope.prototype.$evalAsync = function(expr) {
 this.$$asyncQueue.push({scope: this, expression: expr});
};

我们显式在放入队列的对象上设置当前作用域,是为了使用作用域的继承,在这个系列的下一篇文章中,我们会讨论这个。

然后,我们在$digest中要做的第一件事就是从队列中取出每个东西,然后使用$eval来触发所有被延迟执行的函数:

Scope.prototype.$digest = function() {
 var ttl = 10;
 var dirty;
 do {
  while (this.$$asyncQueue.length) {
   var asyncTask = this.$$asyncQueue.shift();
   this.$eval(asyncTask.expression);
  }
  dirty = this.$$digestOnce();
  if (dirty && !(ttl--)) {
   throw "10 digest iterations reached";
  }
 } while (dirty);
};

这个实现保证了:如果当作用域还是脏的,就想把一个函数延迟执行,那这个函数会在稍后执行,但还处于同一个digest中。

下面是关于如何使用$evalAsync的一个示例:

http://jsbin.com/ilepOwI/1/embed?js,console

作用域阶段

$evalAsync做的另外一件事情是:如果现在没有其他的$digest在运行的话,把给定的$digest延迟执行。这意味着,无论什么时候调用$evalAsync,可以确定要延迟执行的这个函数会“很快”被执行,而不是等到其他什么东西来触发一次digest。

需要有一种机制让$evalAsync来检测某个$digest是否已经在运行了,因为它不想影响到被列入计划将要执行的那个。为此,Angular的作用域实现了一种叫做阶段(phase)的东西,它就是作用域上一个简单的字符串属性,存储了现在正在做的信息。

在Scope的构造函数里,我们引入一个叫$$phase的字段,初始化为null:

function Scope() {
 this.$$watchers = [];
 this.$$asyncQueue = [];
 this.$$phase = null;
}

然后,我们定义一些方法用于控制这个阶段变量:一个用于设置,一个用于清除,也加个额外的检测,以确保不会把已经激活状态的阶段再设置一次:

Scope.prototype.$beginPhase = function(phase) {
 if (this.$$phase) {
  throw this.$$phase + ' already in progress.';
 }
 this.$$phase = phase;
};

Scope.prototype.$clearPhase = function() {
 this.$$phase = null;
};

在$digest方法里,我们来从外层循环设置阶段属性为“$digest”:

Scope.prototype.$digest = function() {
 var ttl = 10;
 var dirty;
 this.$beginPhase("$digest");
 do {
  while (this.$$asyncQueue.length) {
   var asyncTask = this.$$asyncQueue.shift();
   this.$eval(asyncTask.expression);
  }
  dirty = this.$$digestOnce();
  if (dirty && !(ttl--)) {
   this.$clearPhase();
   throw "10 digest iterations reached";
  }
 } while (dirty);
 this.$clearPhase();
};

      

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/219.html