实现 nextTick

简介: 利用微任务与宏任务,派发延时任务。Vue 中 nextTick 的原理。实现 nextTick。

为什么需要 nextTick

在同一段逻辑中,上半段我们更新了视图,下半段又依赖上半段更新完以后的新视图,同步执行上半段逻辑与下半段逻辑,那一定是不可行的,我们需要将下半段逻辑作为一个延时任务放到下一次事件循环中执行。读伪代码也许更有助理解:

function init() {
  createMessageContainer() // 更新视图
  message.open() // 使用新的视图。这里会报错,因为依赖上面更新完后得到的新视图
}

// 于是我们希望可以派发一个延时任务
function init() {
  createMessageContainer() // 更新视图
  nextTick(() => {
    message.open() // 使用新的视图
  })
}

我们希望 nextTick 可以接收一个回调函数,这个回调函数包裹了我们希望在这一次事件循环结束以后执行的逻辑。

那么如何实现这个 nextTick 就是我们讨论的重点。

如何实现 nextTick

nextTick 需要将传入的回调函数包装成异步任务,异步任务分为宏任务与微任务,为了尽快执行,所以应该优先选择微任务。但是我们也需要考虑兼容性,最后我们选用 API 的优先级如下:

  1. Promise
  2. MutationObserver
  3. setImmediate
  4. setTimeout
function nextTick(callback) {
  let timerFunc;
  if (Promise可用) {
    // 使用 Promise
  } else if (MutationObserver可用) {
    // 使用 MutationObserver
  } else if (setImmediate可用) {
    // 使用 setImmediate
  } else {
    // 微任务都不被支持,使用 setTimeout
  }
  timerFunc(); // 派发延时任务
}

有了设计思路,实现起来易如反掌:

function nextTick(callback) {
  let timerFunc;
  if (typeof Promise !== 'undefined') {
    const p = Promise.resolve();
    timerFunc = () => {
      p.then(callback);
    };
  } else if (
    typeof MutationObserver !== 'undefined' &&
    MutationObserver.toString() === '[object MutationObserverConstructor]'
  ) {
    let counter = 1;
    const observer = new MutationObserver(callback);
    const textNode = document.createTextNode(String(counter));
    observer.observe(textNode, { characterData: true });
    timerFunc = () => {
      counter = (counter + 1) % 2;
      textNode.data = String(counter); // 数据更新
    };
  } else if (typeof setImmediate !== 'undefined') {
    timerFunc = () => {
      setImmediate(callback);
    };
  } else {
    timerFunc = () => {
      setTimeout(callback, 0);
    };
  }
  timerFunc();
}

总结

读到这里你应该已经发现以上就是 Vue 中 nextTick 的原理了~

异步任务的巧妙使用可以帮助我们更好的玩转 JavaScript~

相关文章
|
消息中间件 Apache 数据安全/隐私保护
[ActiveMQ]修改默认密码
ActiveMQ使用的是jetty服务器, 在ActiveMQ目录下的conf/jetty.xml文件,vim打开 将property name为authenti...
2475 0
|
C语言 C++
第一个c语言程序
第一个c语言程序
|
关系型数据库 Linux 网络安全
很详细的PostgreSQL安装部署教程
很详细的PostgreSQL安装部署教程
1373 0
|
存储 Java
JavaFX【TableView使用详解】
JavaFX【TableView使用详解】
|
SQL 关系型数据库 MySQL
mysqlbinlog 生产环境问题排查实践
mysqlbinlog 生产环境问题排查实践
227 0
typescript41-class类的私有修饰符
typescript41-class类的私有修饰符
190 0
typescript41-class类的私有修饰符
|
SQL 关系型数据库 MySQL
mysql数据库基本操作(sql语句加注释)
mysql数据库基本操作(sql语句加注释)
705 0
|
MySQL 关系型数据库 Shell
Ansible第二篇:ansible-playbook
一、Playbook语法 Ansible-playbook采用YAML语法编写。 示例:[root@LOCALHOST ~]# cat yaml/httpd.
1687 0
|
JavaScript API 数据格式
Vue 实战 饿了么 (一)
当Vue实例没有el属性时,则该实例尚没有挂载到某个dom中;假如需要延迟挂载,可以在之后手动调用vm.$mount()方法来挂载。
2498 0