美文网首页
简单实现一下notify和nextTick

简单实现一下notify和nextTick

作者: HelenYin | 来源:发表于2021-03-18 23:46 被阅读0次

再来简单的写一下notify和nextTick这部分代码
nextTick的作用就是在每次notify的时候,让watcher的更新操作放到下一个tick,而在本次执行栈中,只是把watcher收集到一个数组里。
这样做的好处:因为watcher每次更新就会重新执行render(生成vnode)和update(生成真实dom),但是有时候一次会给很多data赋值,而多个data服务于同一个watcher的可能性是很大的,所以这种情况我们只用更新一次watcher即可。
从watcher的update方法开始,在没有做队列的时候我是这样写的

// watcher
 update() {
    this.get();
 }

queue
那么我们想把同一个tick的watcher收集到一个队列里,那么就在这里改动,这里把 当前watcher收集进队列,用一个数组queue来存放watcher。这个queue收集当前watcher,在nextTick的回调中清空,因为nextTick就是下一个tick。

has
这里为了不收集相同依赖,所以需要一个容器来存放已经被收集过的watcher,这里我们用一个对象来存放已经收集过的watcher的id,watcher需要添加id,每次新增一个watch,id就加 1,这样一个可以保证watcher的id唯一,并且还可以知道watcher生成的先后顺序,先生成的是后面生成的父级。

waiting
waiting用来控制什么时候执行nextTick。waiting为true时不允许执行nextTick,false的时候可以执行nextTick。那么问题就变成如何控制这个waiting的值。
执行下一个tick之前,waiting是true,nextTick执行waiting变为false。

if (!waiting) {
  waiting = true;
  nextTick(() => {
    waiting = false;
    // ...
  });
}

这里需要调整一下,nextTick的回调里需要做什么逻辑?
首先要知道事件队列这个概念
js引擎遇到一个异步事件后并不会一直等待其返回结果,而是会将这个事件挂起,继续执行执行栈中的其他任务。当一个异步事件返回结果后,js会将这个事件加入与当前执行栈不同的另一个队列,我们称之为事件队列。

也就是说,当我们频繁的在给data设值的时候,程序是不会执行nextTick回调的,程序会先把当前执行栈中的task执行完之后,再去存放异步的任务队列里找任务来执。

现在再来想一下nextTick回调中应该处理什么逻辑
首先执行一次执行收集的watcher的更新
然后,就是重置一些变量,重置的就是上面提到的waiting, has, queue
差不多长这样

let queue = [];
let has = {};
let waiting = false;

function resetSchedulerState() {
  queue = [];
  has = {};
  waiting = false;
}

function flushSchedulerQueue () {
  for (const watcher of queue) {
    watcher.get();
  }
  resetSchedulerState();
}

export function queueWatcher (watcher) {
  const id = watcher.id;
  if (!has[id]) {
    has[id] = true;
    queue.push(watcher);
  }
  if (!waiting) {
    waiting = true;
    nextTick(flushSchedulerQueue);
  }
}

接下来就是实现nextTick
nextTick就很简单
这里我不看MutationObserver(这货只有ie支持)和setImmediate(node里用的多),我简单的实现就是如果支持Promise的浏览器将就用Promise,不支持就用setTimeout
这里nextTick内部也做了waiting相同的处理
timerFunc
用于存放最后用到的nextTick的执行函数,有可能是微任务promise也有可能是宏任务setTimeout,这个在2.5以前做了区分,在2.5版本以后没有做区分了
callbacks
用于存放传入的cb
pending
等同于上面的waiting
这里nextTick内部其实也实现了的功能
长这样

let timerFunc;
let callbacks = [];
let pending = false;

function flushCallbacks () {
  pending = false;
  for (const cb of callbacks) {
    cb();
  }
  pending = false;
  callbacks = [];
}

if (typeof Promise !== 'undefined') {
  const p = Promise.resolve();
  timerFunc = () => {
    p.then(flushCallbacks);
  };
} else {
  timerFunc = () => {
    setTimeout(flushCallbacks);
  };
}

export function nextTick (cb) {
  callbacks.push(cb);
  if (!pending) {
    pending = true;
    timerFunc();
  }
}

实现在这个仓库
https://github.com/TingYinHelen/tempo

相关文章

网友评论

      本文标题:简单实现一下notify和nextTick

      本文链接:https://www.haomeiwen.com/subject/lrepcltx.html