深入理解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()();
}
};
内容版权声明:除非注明,否则皆为本站原创文章。
