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

我们把$apply也修改一下,在它里面也设置个跟自己一样的阶段。在调试的时候,这个会有些用:

Scope.prototype.$apply = function(expr) {
 try {
  this.$beginPhase("$apply");
  return this.$eval(expr);
 } finally {
  this.$clearPhase();
  this.$digest();
 }
};

最终,把对$digest的调度放进$evalAsync。它会检测作用域上现有的阶段变量,如果没有(也没有已列入计划的异步任务),就把这个digest列入计划。

Scope.prototype.$evalAsync = function(expr) {
 var self = this;
 if (!self.$$phase && !self.$$asyncQueue.length) {
  setTimeout(function() {
   if (self.$$asyncQueue.length) {
    self.$digest();
   }
  }, 0);
 }
 self.$$asyncQueue.push({scope: self, expression: expr});
};

有了这个实现之后,不管何时、何地,调用$evalAsync,都可以确定有一个digest会在不远的将来发生。

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

在digest之后执行代码 - $$postDigest

还有一种方式可以把代码附加到digest循环中,那就是把一个$$postDigest函数列入计划。

在Angular中,函数名字前面有双美元符号表示它是一个内部的东西,不是应用开发人员应该用的。但它确实存在,所以我们也要把它实现出来。

就像$evalAsync一样,$$postDigest也能把一个函数列入计划,让它“以后”运行。具体来说,这个函数将在下一次digest完成之后运行。将一个$$postDigest函数列入计划不会导致一个digest也被延后,所以这个函数的执行会被推迟到直到某些其他原因引起一次digest。顾名思义,$$postDigest函数是在digest之后运行的,如果你在$$digest里面修改了作用域,需要手动调用$digest或者$apply,以确保这些变更生效。

首先,我们给Scope的构造函数加队列,这个队列给$$postDigest函数用:

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

然后,我们把$$postDigest也加上去,它所做的就是把给定的函数加到队列里:

Scope.prototype.$$postDigest = function(fn) {
 this.$$postDigestQueue.push(fn);
};

最终,在$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();

 while (this.$$postDigestQueue.length) {
  this.$$postDigestQueue.shift()();
 }
};


      

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

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