$evalAsync是什么
顾名思义,延迟执行一段作用于相应scope的eval代码。于timeout的区别是,timeout会把代码交给浏览器去执行,开发者无法控制到底什么时候执行。$evalAsync运行代码是在digest周期内运行的。具体可以通过这个测试案例看的很明白。
it('$evalAsync', function() {
scope.aValue = 123;
scope.asyncEvaluated = false;
scope.asyncEvaluatedImmediately = false;
var watchFn = function(scope){
return scope.aValue;
}
var listenFn = function(newvalue,oldvalue,scope){
scope.$evalAsync(function(scope){
scope.asyncEvaluated = true;//随后执行的
})
scope.asyncEvaluatedImmediately = scope.asyncEvaluated;//立即执行的,这时候应该是false
}
scope.$watch(watchFn,listenFn);
scope.$digest();
expect(scope.asyncEvaluated).toBe(true);
expect(scope.asyncEvaluatedImmediately).toBe(false);
});
实现思路
首先在scope里面创建一个延迟eval队列。
function Scope() {
this.$$watchers = [];
this.$$lastDirtyWatch = null;
this.$$asyncQueue = [];
}
调用$evalAsync的时候就把要执行的函数推入这个队列。
Scope.prototype.$evalAsync = function (expr) {
this.$$asyncQueue.push({ scope: this, expression: expr });
}
在每次digest之前都把这个队列里的函数用当前scope执行一下
Scope.prototype.$digest = function () {
var dirty;
var ttl = 10;
do {
while (this.$$asyncQueue.length) {
var asyncTask = this.$$asyncQueue.shift();
asyncTask.scope.$eval(asyncTask.expression);
}
dirty = this.$$digestOnce();
if (dirty && !(ttl--)) {
throw "digest次数到达上限依然不稳定"
}
} while (dirty);
}
为什么这个while循环放在了digestOnce的前面?
注意的是,这里的while循环放到了$$digestOnce方法的前面,需要解释一下为什么放到了前面:
第一次执行这个digest的时候,是初始化的时候,值肯定是脏的,这时候$$asyncQueue里面是没有东西的,所以直接运行$$digestOnce,运行$$digestOnce的时候,才会执行watcher里面的listenFn,也就是这时候才会把asyncEval注册的方法推入队列,然后scope.asyncEvaluatedImmediately = scope.asyncEvaluated;
这段代码会运行,当然是false的。
运行完了以后,跳出digestOnce,因为linstenFn被运行了,所以肯定返回的是dirty,那么就会进行下一次循环,在这次新的循环里,再去运行async队列里面的函数。
如果放在digestOnce后面会怎么样?
详情可以看自己录的自言自语的视频。
网友评论