Vue 中批量异步更新 nextTick 解读

批量更新代码示例:

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 操作进行更新。

你可能感兴趣的:(Vue 中批量异步更新 nextTick 解读)