ng中说到事件轮询,第一个拿出来的肯定是digest函数啦,在digest()的事件轮询中,一共会轮询两个列表,一个为AsyncEvalQueue,一个为Watchers列表,apply方法进入ng上下文执行的Callback fn将其context修改为ng的控制域,先从queue取出每一个asyncTask,获取其scope通过*eval方法进行callbackfn目标的expression计算,以及修改值引起$watch的调用,导致监听器被触发,dirtycheck在digest()中执行,遍历watchers列表,当有newvalue出现时触发监听器listenerFn的回调修改model并重新绘制dom节点,然后再将脏值置位,即还要进行一次dirtycheck才可以判断数据已经稳定,当且仅当数据稳定时digest才算执行完毕,即queue为空且watchers列表的所有值的脏值都为false,即不需要进行过多的检测了,已经稳定(ttl=10)最多检测次数为ttl设定值。
代码如下:(AsyncQueue的遍历读取)
lastDirtyWatch = null;
do { // "while dirty" loop
dirty = false;
current = target;
//下面开始对异步队列进行轮询处理
//在promise进行回调操作就是基于这个轮询
while(asyncQueue.length) {
try {
asyncTask = asyncQueue.shift();
asyncTask.scope.$eval(asyncTask.expression);
} catch (e) {
clearPhase();
$exceptionHandler(e);
}
lastDirtyWatch = null;
}
//这里就是著名的脏值检测啦,把所有的watchers都轮询一遍
(dirty check)
do { // "traverse the scopes" loop
if ((watchers = current.$$watchers)) {
// process our watches
length = watchers.length;
while (length--) {
try {
watch = watchers[length];
// Most common watches are on primitives, in which case we can short
// circuit it with === operator, only when === fails do we use .equals
if (watch) {
if ((value = watch.get(current)) !== (last = watch.last) &&
!(watch.eq
? equals(value, last)
: (typeof value == 'number' && typeof last == 'number'
&& isNaN(value) && isNaN(last)))) {
dirty = true;
lastDirtyWatch = watch;
watch.last = watch.eq ? copy(value) : value;
watch.fn(value, ((last === initWatchVal) ? value : last), current);
//ttl的一个判断
if (ttl < 5) {
logIdx = 4 - ttl;
if (!watchLog[logIdx]) watchLog[logIdx] = [];
logMsg = (isFunction(watch.exp))
? 'fn: ' + (watch.exp.name || watch.exp.toString())
: watch.exp;
logMsg += '; newVal: ' + toJson(value) + '; oldVal: ' + toJson(last);
watchLog[logIdx].push(logMsg);
}
if (watch.get.$$unwatch) stableWatchesCandidates.push({watch: watch, array: watchers});
} else if (watch === lastDirtyWatch) {
// If the most recently dirty watcher is now clean, short circuit since the remaining watchers
// have already been tested.
dirty = false;
break traverseScopesLoop;
}
}
} catch (e) {
clearPhase();
$exceptionHandler(e);
}
}
}