我的编程空间,编程开发者的网络收藏夹
学习永远不晚

vue中的任务队列和异步更新策略(任务队列,微任务,宏任务)

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

vue中的任务队列和异步更新策略(任务队列,微任务,宏任务)

事件循环

JavaScript 语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。

为了协调事件、用户交互、脚本、UI 渲染和网络处理等行为,防止主线程的不阻塞,Event Loop 的方案应用而生。

Event Loop 包含两类:

  • 一类是基于 Browsing Context
  • 一种是基于 Worker

二者的运行是独立的,也就是说,每一个 JavaScript 运行的"线程环境"都有一个独立的 Event Loop,每一个 Web Worker 也有一个独立的 Event Loop。

任务队列

vue 数据驱动视图是数据改变,视图异步等待所有数据变化完成,统一进行视图更新。

既然是异步就有顺序和优先级, 异步任务队列是那种顺序执行 ?

tip: 微任务优先级高于宏任务 MDN 介绍

任务队列主要分为两种:

1、microtasks(微任务):

  • Promise :ES6的异步处理方案
  • process.nextTick(vue.nextTick) :下轮tick更新机制,
  • Mutation Observer API: DOM改变 监听 API

2、macrotasks(宏任务也称任务):

  • setTimeout() : 延时器
  • setInterval(): 计时器
  • setImmediate:node.js 回调函数延迟执行,process.nextTicl() 方法十分类似

process.nextTick()中的回调函数执行的优先级要高于setImmediate().这里的原因在于事件循环对观察者的检查是有先后顺序的,process.nextTick()属于idle观察者,setImmediate()属于check观察者。在每一个轮循环检查中,idle观察者先于I/O观察者,I/O观察者先于check观察者。

在具体实现上,process.nextTick()的回调函数保存在一个数组中,setImmediate()的结果则是保存在链表中。在行为上,process.nextTick()在每轮循环中会将该数组中的回调函数全部执行完,而setImmediate()在每轮循环中执行链表中的一个回调函数

  • I/O :系统IO(input/output)
  • UI render :页面渲染
  • requestAnimationFrame():异步动画渲染 ,浏览器调用指定的函数以在下次重绘之前更新动画

如何理解微任务和宏任务?

创造代码的人也是人,他们的灵感多数来自于生活。我们这里打个比方(朴灵老师也这样比喻),javascript处理异步就像去餐馆吃饭,服务员会先为顾客下单,下完单把顾客点的单子给后厨让其准备,然后就去服务下一位顾客,,而不是一直等待在出餐口。

javascript将顾客下单的行为进行了细分。无外乎两种酒水类和非酒水类。对应着我们javascript中的macroTask和microTask。

但是在不同场景下的步骤是不一样的,就像西餐和中餐。西餐划分的非常详细:头盘->汤->副菜->主菜->蔬菜类菜肴->甜品->咖啡,中餐就相对简单许多:凉菜->热菜->汤。

任务队列,下面看几个示例, 输出内容便是执行顺序:

任务队列,下面看几个示例, 输出内容便是执行顺序:

setTimeout(()=>{
    console.log('1')
    Promise.resolve().then(function() {
        console.log('2')
    })
}, 0)
setTimeout(()=>{
    console.log('3')
    Promise.resolve().then(function() {
        console.log('4')
    })
}, 0)
setTimeout(function() {console.log('6')}, 0)
requestAnimationFrame(function(){
    console.log('5')
})
setTimeout(function() {console.log('7')}, 0)
new Promise(function executor(resolve) {
    console.log('1')
    resolve()
    console.log('2')
}).then(function() {
    console.log('4')
})
console.log('3')
console.log('1');
setTimeout(() => {
    console.log('5');
    process.nextTick(() => console.log('6'));
}, 0);
process.nextTick(() => {
    console.log('3');
    process.nextTick(() => console.log('4'));
});
console.log('2');

这样就可以理解Vue的异步更新策略运行机制

  created() {
    this.textDemo()
  },
  methods: {
    textDemo() {
      console.log(1)
      setTimeout(() => { // macroTask
        console.log(4)
        setTimeout(() => { // macroTask
          console.log(8)
        }, 0)
        this.$nextTick(() => { // microTask
          console.log(5)
        })
        Promise.resolve().then(function() { // microTask
          console.log(7)
        })
        this.$nextTick(() => { // microTask
          console.log(6)
        })
      }, 0)
      this.$nextTick(() => { // microTask
        console.log(3)
      })
      console.log(2)
    },
}

到此我们已经知道代码是如何运行的了, 如果你想要更深入理解, 请继续向下阅读。

深究Vue异步更新策略原理

我们先看一个示例:

<template>
  <div>
    <div ref="test">{{test}}</div>
    <button @click="handleClick">tet</button>
  </div>
</template>
<script>
export default {
    data () {
        return {
            test: 'begin'
        };
    },
    methods () {
        handleClick () {
            this.test = 'end';
            console.log(this.$refs.test.innerText);// 结果输出 begin
        }
    }
}
</script>

通过上面示例可以看出 Vue是异步执行DOM更新, 更新会缓冲到队列中, 在nextTick 集中刷新队列并执行实际 (已去重的) 工作

当然新版 this.$nextTick 有一些变化 。从Vue 2.5+开始,抽出来了一个单独的文件next-tick.js来执行它。

在这里插入图片描述

其大概的意思就是:在Vue 2.4之前的版本中,nextTick 几乎都是基于 microTask 实现的(具体可以看文章nextTick一节),但是由于 microTask 的执行优先级非常高,在某些场景之下它甚至要比事件冒泡还要快,就会导致一些诡异的问题;但是如果全部都改成 macroTask,对一些有重绘和动画的场景也会有性能的影响。所以最终 nextTick 采取的策略是默认走 microTask,对于一些DOM的交互事件,如 v-on绑定的事件回调处理函数的处理,会强制走 macroTask。

具体做法就是,在Vue执行绑定的DOM事件时,默认会给回调的handler函数调用withMacroTask方法做一层包装,它保证整个回调函数的执行过程中,遇到数据状态的改变,这些改变而导致的视图更新(DOM更新)的任务都会被推到macroTask。

源码:

function add$1 (event, handler, once$$1, capture, passive) {
    handler = withMacroTask(handler);
    if (once$$1) { handler = createOnceHandler(handler, event, capture); }
    target$1.addEventListener(
        event,
        handler,
        supportsPassive
        ? { capture: capture, passive: passive }
        : capture
    );
}

function withMacroTask (fn) {
    return fn._withTask || (fn._withTask = function () {
        useMacroTask = true;
        var res = fn.apply(null, arguments);
        useMacroTask = false;
        return res
    })
}

最后,写一段DEMO验证一下 :

<template>
    <div>
        <button @click="handleClick">change</button>
    </div>
</template>
<script>
export default {
   created() {
      this.handleClick() //  得出结果 : 2 3 1 4
  },
   methods: {
       handleClick() {
         setTimeout(() => { // macroTask
           console.log(4)
         }, 0)
    
         this.$nextTick(() => { // microTask
           console.log(2)
         })
    
         Promise.resolve().then(function() { // microTask
           console.log(1)
         })
    
         this.$nextTick(() => { // microTask
           console.log(3)
         })
       }
   }
}
</script>

在Vue 2.5+中,这段代码的输出顺序是1 - 2 - 3 - 4, 而 Vue 2.4 和 不通过DOM 输出 2 - 3 - 1 - 4。nextTick执行顺序的差异正好说明了上面的改变。

tips: 所以这里需要留意遇到DOM操作, 同步执行受阻或者节点内容未及时更新可以使用 this.$nextTick 等待一下在执行下面的操作。

<div id="example">
    <audio ref="audio"
           :class="lazy" data-src="url"></audio>
    <span ref="url"></span>
    <button @click="changeUrl">click me</button>
</div>
<script>
const musicList = [
    'http://sc1.111ttt.cn:8282/2017/1/11m/11/304112003137.m4a?tflag=1519095601&pin=6cd414115fdb9a950d827487b16b5f97#.mp3',
    'http://sc1.111ttt.cn:8282/2017/1/11m/11/304112002493.m4a?tflag=1519095601&pin=6cd414115fdb9a950d827487b16b5f97#.mp3',
    'http://sc1.111ttt.cn:8282/2017/1/11m/11/304112004168.m4a?tflag=1519095601&pin=6cd414115fdb9a950d827487b16b5f97#.mp3'
];
var vm = new Vue({
    el: '#example',
    data: {
        index: 0,
        url: ''
    },
    methods: {
        changeUrl() {
            this.index = (this.index + 1) % musicList.length
            this.url = musicList[this.index];
            this.$refs.audio.play();
        }
    }
});
</script>

毫无悬念,这样肯定是会报错的:

Uncaught (in promise) DOMException: The element has no supported sources.

原因就在于audio.play()是同步的,而这个时候DOM更新是异步的,class="lazy" data-src属性还没有被更新,结果播放的时候class="lazy" data-src属性为空,就报错了。

解决办法就是在play的操作加上this.$nextTick()。

this.$nextTick(function() {
    this.$refs.audio.play();
});

异步更新有什么好处?

<template>
  <div>
    <div>{{test}}</div>
  </div>
</template>
<script>
export default {
    data () {
        return {
            test: 0
        };
    },
    mounted () {
      for(let i = 0; i < 1000; i++) {
        this.test++;
      }
    }
}
</script>

上面的例子非常直观,可以这么理解当数据更新同步操作DOM会出现频繁渲染视图造成页面卡顿,极端的消耗资源。所以异步更新大大提升了性能, 并且数据更新很高效体验并没有降低。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

vue中的任务队列和异步更新策略(任务队列,微任务,宏任务)

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

vue中的任务队列和异步更新策略(任务队列,微任务,宏任务)

这篇文章主要介绍了vue中的任务队列和异步更新策略(任务队列,微任务,宏任务),具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
2022-11-13

golang异步任务队列怎么实现

在Go语言中,可以使用goroutine和channel来实现异步任务队列。下面是一个简单的示例代码:package mainimport ("fmt""sync""time")func main() {// 创建一个无缓冲的chann
2023-10-27

如何解决Go语言中的并发任务的分布式任务队列和任务调度策略问题?

如何解决Go语言中的并发任务的分布式任务队列和任务调度策略问题?引言:在分布式系统中,任务的分发和调度是一个关键问题。在Go语言中,通过使用并发技术可以有效地管理和执行任务。本文将介绍如何使用分布式任务队列和任务调度策略来解决Go语言中的并
2023-10-22

golang异步任务队列问题怎么解决

在Go语言中,可以使用goroutine和channel来解决异步任务队列的问题。下面是一个简单的示例代码,演示了如何使用goroutine和channel来实现异步任务队列:gopackage mainimport ("fmt""sync
2023-10-20

Redis数据库队列怎么实现异步任务

在Redis中实现异步任务可以通过Redis的列表数据结构来实现队列。下面是一种常见的实现方式:生产者将需要执行的任务加入到Redis列表中,例如将任务数据作为消息推送到队列中。消费者从Redis列表中获取任务并执行,处理完任务后将结果存
Redis数据库队列怎么实现异步任务
2024-04-22

如何处理Go语言中的并发任务的任务队列和任务优先级问题?

如何处理Go语言中的并发任务的任务队列和任务优先级问题?在Go语言的并发编程中,任务队列和任务优先级是两个常见的问题。本文将介绍如何处理这两个问题,并提供具体的代码示例。一、任务队列问题任务队列常用于处理大量的任务,并按顺序逐个执行。在Go
2023-10-22

如何在PHP开发中实现异步任务和消息队列?

如何在PHP开发中实现异步任务和消息队列?随着互联网的高速发展,网站的访问量和数据处理量越来越大。为了提高用户体验和系统的稳定性,开发人员不得不考虑如何高效地处理大量的并发请求和耗时任务。异步任务和消息队列成为了解决这个问题的有效手段。异步
如何在PHP开发中实现异步任务和消息队列?
2023-11-03

如何利用Redis和Kotlin开发异步任务队列功能

如何利用Redis和Kotlin开发异步任务队列功能引言:随着互联网的发展,异步任务的处理变得越来越重要。在开发过程中,经常会遇到一些需要耗时的任务,例如发送邮件、处理大数据等等。为了提高系统的性能和可伸缩性,我们可以使用异步任务队列来处理
2023-10-22

队列在PHP与MySQL中的任务监控和任务调度的实现方案

引言在现代的Web应用程序开发中,任务队列是非常重要的一项技术。通过队列,我们可以将一些需要在后台执行的任务排队,并通过任务调度来控制任务的执行时间和顺序。本文将介绍如何在PHP与MySQL中实现任务的监控和调度,并提供具体的代码示例。一、
2023-10-21

队列的延迟任务和定时任务在PHP与MySQL中的应用场景

引言:随着互联网的发展,对于实时性和高并发的需求越来越高,为了应对这些需求,我们常常需要使用到队列的延迟任务和定时任务。本文将重点介绍在PHP和MySQL中,如何应用队列的延迟任务和定时任务,并给出具体的代码示例。一、队列的延迟任务的应用场
2023-10-21

Node事件循环中的微任务队列是什么

这篇文章主要介绍“Node事件循环中的微任务队列是什么”,在日常操作中,相信很多人在Node事件循环中的微任务队列是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Node事件循环中的微任务队列是什么”的疑
2023-07-06

深入浅析Node事件循环中的微任务队列

在 之前的文章 中,我们了解到事件循环是 Node.js 的一个关键部分,用于协调同步和异步代码的执行。它由六个不同的队列组成。一个 nextTick 队列和一个 Promise 队列(被称为微任务队列)、一个计时器队列、一个 I/O 队列、一个检查队列,最后是关闭队列。
2023-05-14

如何利用Redis和Rust语言实现异步任务队列功能

如何利用Redis和Rust语言实现异步任务队列功能引言:在当今高并发的互联网应用中,异步任务队列是非常常见和实用的功能。它可以将耗时较长的任务从主线程异步处理,提高系统的吞吐能力和响应速度。本文将介绍如何利用Redis和Rust语言实现一
2023-10-22

Golang中使用RabbitMQ实现任务队列的优化技巧

在Golang中使用RabbitMQ实现任务队列时,可以使用以下技巧进行优化:1. 使用消息确认机制:在消费者处理完任务后,手动确认消息的消费完成。这可以确保消息被正确处理,避免重复消费或丢失消息。2. 设置预取计数:使用`channel.
2023-10-08

Python环境下安装使用异步任务队列包Celery的基础教程

1.简介 celery(芹菜)是一个异步任务队列/基于分布式消息传递的作业队列。它侧重于实时操作,但对调度支持也很好。 celery用于生产系统每天处理数以百万计的任务。 celery是用Python编写的,但该协议可以在任何语言实现。它也
2022-06-04

Java中如何实现消息队列任务的平滑关闭

这篇文章主要介绍Java中如何实现消息队列任务的平滑关闭,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!前言消息队列中间件是分布式系统中重要的组件,主要解决应用解耦,异步消息,流量削锋等问题,实现高性能,高可用,可伸缩
2023-05-30

队列技术在PHP与MySQL中的异步任务处理和消息回调机制的应用

随着互联网的快速发展,用户对于网站和应用的需求也越来越高。为了提高用户体验和应对高并发访问的需求,异步任务处理和消息回调机制成为了开发中不可或缺的一环。本文将介绍如何使用队列技术,在PHP与MySQL中实现异步任务处理和消息回调机制,并提供
2023-10-21

编程热搜

目录