30 # 宏任务和微任务的区分

简介: 30 # 宏任务和微任务的区分

可以通过 vue 的源码 nextTick 方法:里面描述了浏览器中的常见的宏任务和微任务

  • 宏任务:script、ui、setTimeout、setInterval、requestFrameAnimation、setImmediate(ie 10以上、node 中支持)、MessageChannel(消息通道)都是异步的,还有 click,ajax
  • 微任务:语言本身提供的 promise.then、mutationObserver、nextTick

【nextTick 源码:我这里用 2.6.11 版本的】https://github.com/vuejs/vue/blob/v2.6.11/src/core/util/next-tick.js

Vue 在内部对异步队列判断当前环境优先支持的异步方法,优先选择微任务,其顺序如下:

  1. 先判断当前环境是否原生支持 promise
  2. 如果不支持 promise, 就判断是否支持 MutationObserver,不是IE环境,并且原生支持 MutationObserver
  3. 判断当前环境是否原生支持 setImmediate
  4. 以上三种都不支持就选择 setTimeout(setTimeout 可能产生一个 4ms 的延迟,而 setImmediate 会在主线程执行完后立刻执行)

优先级: Promise —> MutationObserver —> setImmediate—> setTimeout

/* @flow */
/* globals MutationObserver */
// noop 表示一个无操作空函数,用作函数默认值,防止传入 undefined 导致报错
import { noop } from "shared/util";
// 错误处理函数
import { handleError } from "./error";
// 环境判断函数,isNative 判断某个属性或方法是否原生支持,如果不支持或通过第三方实现支持都会返回false
import { isIE, isIOS, isNative } from "./env";
// 标记 nextTick 最终是否以微任务执行
export let isUsingMicroTask = false;
// 存放调用 nextTick 时传入的回调函数
const callbacks = [];
// 标记是否已经向任务队列中添加了一个任务,如果已经添加了就不能再添加了
// 当向任务队列中添加了任务时,将 pending 置为true, 当任务被执行时将 pending 置为 false
let pending = false;
// flushCallbacks 函数用来遍历 callbacks 数组的拷贝并执行其中的回调
function flushCallbacks() {
    pending = false;
    // 拷贝一份 callbacks
    const copies = callbacks.slice(0);
    // 然后清空 callbacks
    callbacks.length = 0;
    // 遍历执行传入的回调
    for (let i = 0; i < copies.length; i++) {
        copies[i]();
    }
}
// Here we have async deferring wrappers using microtasks.
// In 2.5 we used (macro) tasks (in combination with microtasks).
// However, it has subtle problems when state is changed right before repaint
// (e.g. #6813, out-in transitions).
// Also, using (macro) tasks in event handler would cause some weird behaviors
// that cannot be circumvented (e.g. #7109, #7153, #7546, #7834, #8109).
// So we now use microtasks everywhere, again.
// A major drawback of this tradeoff is that there are some scenarios
// where microtasks have too high a priority and fire in between supposedly
// sequential events (e.g. #4521, #6690, which have workarounds)
// or even between bubbling of the same event (#6566).
let timerFunc;
// The nextTick behavior leverages the microtask queue, which can be accessed
// via either native Promise.then or MutationObserver.
// MutationObserver has wider support, however it is seriously bugged in
// UIWebView in iOS >= 9.3.3 when triggered in touch event handlers. It
// completely stops working after triggering a few times... so, if native
// Promise is available, we will use it:
/* istanbul ignore next, $flow-disable-line */
if (typeof Promise !== "undefined" && isNative(Promise)) {
    const p = Promise.resolve();
    timerFunc = () => {
        // 用 promise.then 把 flushCallbacks 函数包裹成个异步微任务
        p.then(flushCallbacks);
        // In problematic UIWebViews, Promise.then doesn't completely break, but
        // it can get stuck in a weird state where callbacks are pushed into the
        // microtask queue but the queue isn't being flushed, until the browser
        // needs to do some other work, e.g. handle a timer. Therefore we can
        // "force" the microtask queue to be flushed by adding an empty timer.
        // 因为在 ios 下 promise.then 后面没有宏任务的话,微任务队列不会刷新,这里的 setTimeout 是用来强制刷新微任务队列
        if (isIOS) setTimeout(noop);
    };
    // 标记当前 nextTick 使用的微任务
    isUsingMicroTask = true;
} else if (
    !isIE &&
    typeof MutationObserver !== "undefined" &&
    (isNative(MutationObserver) ||
        // PhantomJS and iOS 7.x
        MutationObserver.toString() === "[object MutationObserverConstructor]")
) {
    // Use MutationObserver where native Promise is not available,
    // e.g. PhantomJS, iOS7, Android 4.4
    // (#6466 MutationObserver is unreliable in IE11)
    let counter = 1;
    // new 一个 MutationObserver 类
    const observer = new MutationObserver(flushCallbacks);
    // 创建一个文本节点
    const textNode = document.createTextNode(String(counter));
    // 监听这个文本节点,当数据发生变化就执行 flushCallbacks
    observer.observe(textNode, {
        characterData: true
    });
    timerFunc = () => {
        counter = (counter + 1) % 2;
        // 更新数据
        textNode.data = String(counter);
    };
    // 标记当前 nextTick 使用的微任务
    isUsingMicroTask = true;
} else if (typeof setImmediate !== "undefined" && isNative(setImmediate)) {
    // Fallback to setImmediate.
    // Technically it leverages the (macro) task queue,
    // but it is still a better choice than setTimeout.
    timerFunc = () => {
        setImmediate(flushCallbacks);
    };
} else {
    // Fallback to setTimeout.
    timerFunc = () => {
        setTimeout(flushCallbacks, 0);
    };
}
// 声明 nextTick 函数,接收一个回调函数和一个执行上下文作为参数
export function nextTick(cb?: Function, ctx?: Object) {
    let _resolve;
    // 将传入的回调函数存放到数组中,后面会遍历执行其中的回调
    callbacks.push(() => {
        if (cb) {
            try {
                cb.call(ctx);
            } catch (e) {
                handleError(e, ctx, "nextTick");
            }
        } else if (_resolve) {
            _resolve(ctx);
        }
    });
    // 当在同一轮事件循环中多次调用 nextTick 时,timerFunc 只会执行一次
    if (!pending) {
        pending = true;
        timerFunc();
    }
    // 如果没有传入回调,并且当前环境支持 Promise,就返回一个 Promise,在返回的这个 promise.then 中 DOM 已经更新好了
    // $flow-disable-line
    if (!cb && typeof Promise !== "undefined") {
        return new Promise((resolve) => {
            _resolve = resolve;
        });
    }
}
目录
相关文章
|
机器学习/深度学习 分布式计算 数据处理
分布式计算框架:并行力量的交响乐章
分布式计算框架如Apache Spark解决单机计算挑战,通过拆分任务到多机并行处理提升效率。Spark以其内存计算加速处理,支持批处理、查询、流处理和机器学习。以下是一个PySpark统计日志中每日UV的示例,展示如何利用SparkContext、map和reduceByKey进行数据聚合分析。这些框架的运用,正改变大数据处理领域,推动数据分析和机器学习的边界。【6月更文挑战第18天】
472 2
|
网络协议 安全 Unix
深入剖析进程间通信:Unix 套接字、共享内存与IP协议栈的性能比较
深入剖析进程间通信:Unix 套接字、共享内存与IP协议栈的性能比较
393 2
|
JavaScript 前端开发 数据安全/隐私保护
Vue封装组件并发布到npm仓库
前言 使用Vue框架进行开发,组件封装是一个很常规的操作。一个封装好的组件可以在项目的任意地方使用,甚至我们可以直接从npm仓库下载别人封装好的组件来进行使用,比如iview、element-ui这一类的组件库。但是每个公司的业务场景可能不同,开发人员还是得必须封装自己得组件,如果换了一个项目,那么我们就只能复制组件代码到新的项目里面去了,这样稍显麻烦,其实我们可以将组建上传到npm仓库,要用的时候可以直接从npm安装使用。 总结下来有两点好处: 方面使用,任何项目无缝衔接。 可作为开源项目,积累经验。
845 0
Vue封装组件并发布到npm仓库
|
SQL 运维 监控
关系型数据库性能监控工具
【5月更文挑战第21天】
262 2
|
开发工具 git Windows
IDEA如何对比不同分支某个文件的差异
【9月更文挑战第28天】该指南介绍了在IDEA中使用Git工具窗口进行分支对比的方法。首先,通过底部工具栏或菜单打开Git窗口;接着,在“Branches”选项卡中查看所有分支;然后选择要对比的分支和文件,并通过右键菜单启动对比;最后,在“Diff”视图中查看详细差异,包括新增和删除内容的颜色标记。此外,还提供了使用内置终端执行`git diff`命令进行对比的可选方法。
2222 4
|
人工智能 数据挖掘 决策智能
跟着我的步骤,轻松打造出 AI 智能体
跟着我的步骤,轻松打造出 AI 智能体
431 3
跟着我的步骤,轻松打造出 AI 智能体
|
人工智能 JSON 文字识别
印刷文字识别使用问题之如何数电发票进行识别
印刷文字识别产品,通常称为OCR(Optical Character Recognition)技术,是一种将图像中的印刷或手写文字转换为机器编码文本的过程。这项技术广泛应用于多个行业和场景中,显著提升文档处理、信息提取和数据录入的效率。以下是印刷文字识别产品的一些典型使用合集。
|
编解码 人工智能 物联网
CogVLM2: 智谱开源新一代多模态大模型!
智谱·AI推出了新一代 CogVLM2 系列模型,并开源了使用 Meta-Llama-3-8B-Instruct 构建的两个模型。 与上一代CogVLM开源模型相比,CogVLM2系列开源模型有了很多改进...
|
XML Java 数据格式
如何在Filter中使用Spring容器中的Bean?
如何在Filter中使用Spring容器中的Bean?
465 0
|
小程序 Java 关系型数据库
基于Java微信小程序宠物店商城系统设计和实现(源码+LW+调试文档+讲解等)
基于Java微信小程序宠物店商城系统设计和实现(源码+LW+调试文档+讲解等)