let uid = 0; class Watcher { constructor () { this.id = ++uid; } update () { console.log('watch' + this.id + ' update'); queueWatcher(this); } run () { console.log('watch' + this.id + '视图更新啦~'); } } let callbacks = []; let pending = false; function nextTick (cb) { callbacks.push(cb); if (!pending) { pending = true; setTimeout(flushCallbacks, 0); } } function flushCallbacks () { pending = false; const copies = callbacks.slice(0); callbacks.length = 0; for (let i = 0; i < copies.length; i++) { copies[i](); } } let has = {}; let queue = []; let waiting = false; function flushSchedulerQueue () { let watcher, id; for (index = 0; index < queue.length; index++) { watcher = queue[index] id = watcher.id; has[id] = null; watcher.run(); } waiting = false; } function queueWatcher(watcher) { const id = watcher.id; if (has[id] == null) { has[id] = true; queue.push(watcher); if (!waiting) { waiting = true; nextTick(flushSchedulerQueue); } } } (function () { let watch1 = new Watcher(); let watch2 = new Watcher(); watch1.update(); watch1.update(); watch2.update(); })();
解读:
当 setter 的时候会将对应的 watcher push 到 queue 中,然后通过一个开关变量 waiting 执行 nextTick,而 nextTick 会使用 setTimeout 开启一个定时器,这个定时器将遍历 queue 中的 watcher 进行 run 操作。这些操作由于是在定时器中执行,所以会移交到主线程执行完毕之后的 event loop 中执行。而在主线程中会继续有其它 setter 操作,往 queue 中 push 其它的 watcher,只是此时开关 waiting 已经关闭,所以不会执行新的 nextTick,也就不会开启更多的定时器,一次 tick 就只有一个 setTimeout 定时器。当主线程中所有 setter 操作执行完毕后,queue 中也就有了这次要更新的所有 watcher,然后等待主线程中的代码执行完毕开始执行 event loop,event loop 中遍历 queue 执行所有 watcher 的 run 后,会把开关 waiting 开启,然后开始等下下一轮的 setter 操作进行更新。