JavaScript的宏任务和微任务示例详解

 更新时间:2026年03月17日 09:00:57   作者:辰风沐阳  
在JavaScript中宏任务和微任务是任务队列中两个不同的任务类型,它们是JavaScript异步编程机制的重要组成部分,这篇文章主要介绍了JavaScript宏任务和微任务的相关资料,需要的朋友可以参考下

1. 前言

在 JavaScript 中,任务可以分为同步任务、异步任务,而异步任务又进一步细分为宏任务和微任务

  • 同步任务:指那些在主线程上排队执行的任务,只有前一个任务执行完毕后,才能开始执行下一个任务
  • 异步任务:异步任务不会进入主线程,而是进入 “任务队列” 中等待执行

JavaScript 语言本身是单线程,这意味着在任何时刻,JavaScript 引擎中只有一条主线程来处理所有的任务

2. 同步任务

同步任务只有前一个任务执行完毕后,才能开始执行下一个任务,特点是按照顺序执行,会阻塞后续代码的执行

console.log(1);
console.log(2);

3. 异步任务

异步任务是只有当主线程上的同步任务全部执行完毕后,才会从任务队列中取出异步任务进入主线程执行

console.log(1); // 同步任务
setTimeout(() => console.log(3), 0) // 异步任务,等待同步任务执行完毕之后,才会执行
console.log(2); // 同步任务

宏任务

宏任务是由宿主环境发起的异步任务,通常包含较长的等待时间或需要等待 I/O 操作完成

常见的宏任务:setTimeout / setInterval、ajax 请求、dom 事件

微任务

微任务通常用于处理 Promise 或其他需要尽快执行的操作

常见的微任务:Promise.then、async / await

setTimeout(() => console.log('定时器 - 宏任务'), 0)
Promise.resolve().then(value => {
    console.log('Promise - 微任务');
})
console.log('123 - 同步任务')
// 123 - 同步任务
// Promise - 微任务
// 定时器 - 宏任务

4. 定时器的任务编排

在 JavaScript 中,setTimeout 函数的延时时间的最小值通常是 4 毫秒

这是因为在某些浏览器或操作系统中,为了优化性能和节能,定时器的实际执行可能会有一定的最小延迟

// 即使设置了非常小的延时,浏览器也可能不会立即执行定时器回调函数,而是等待至少 4 毫秒后才执行
setTimeout(() => console.log('定时器'), 0)

设置定时器延时 200 毫秒执行,那么它真的就会 200 毫秒后立即执行吗 ?通过以下代码可以发现并不是这样的

代码解析原因:因为定时器是异步任务,for 循环是同步任务,所以定时器要等 for 循环执行完之后才会执行定时器

定时器的任务编排原理:到达延时时间会先将其放到宏任务队列中,等待主线程处理完同步任务,再来处理异步任务

console.log(new Date().getTime() / 1000);
setTimeout(() => {
    console.log('定时器:' + new Date().getTime() / 1000);
}, 200)
console.log('同步任务')
for (let i = 0; i < 20000; i++) {
    console.log('');
}

5. Promise 微任务处理逻辑

Promise 是微任务中的典型代表,Promise 的构造函数代码是立即执行的,也就是说它是同步代码

// 4. 定时器,放入宏任务队列
setTimeout(() => console.log('定时器'))

new Promise(resolve => {
    // 1. 这里是 promise 的构造函数,它是同步代码
    console.log('promise');
    resolve()
}).then(() => {
    // 3. 这里的代码会放到微任务队列
    console.log('then');
})

// 2. 同步代码
console.log('hello');

// 由于任务执行顺序是:同步任务 > 微任务 > 宏任务,所以上面代码的输出顺序为
// promise
// hello
// then
// 定时器

如果在定时器内部再放入 Promise 和 定时器,那么执行顺序是怎么样的 ?

setTimeout(() => {
    // 1. 同步代码
    console.log('定时器')
    // 因为它是在定时器内部,所以它在宏任务执行之后才会放入任务队列,才会执行
    new Promise(resolve => {
        // 2. 同步代码
        console.log('timeout promise');
        resolve()
    }).then(() => {
        // 3. 微任务
        console.log('timeout then');
    })
    // 4. 宏任务
    setTimeout(() => console.log('定时器内部的定时器'))
})
new Promise(resolve => {
    console.log('promise');
    resolve()
}).then(() => {
    console.log('then');
})
console.log('hello');

// 其实很简单,在上一段代码的结果上,继续按照任务执行优先顺序执行
// promise
// hello
// then
// 定时器
// timeout promise
// timeout then
// 定时器内部的定时器

6. DOM 渲染任务

浏览器在解析页面时,也是按照顺序从上往下解析的,如下所示,先解析 utils.js,然后再渲染 h1 标签

也可以理解为先将解析 utils.js 的任务执行完,再执行渲染 h1 标签的任务

<head>
    <script src="./utils.js"></script>
</head>
<body>
    <h1>JavaScript 的宏任务和微任务</h1>
</body>

如果执行 utils.js 的时间比较长,那么页面将迟迟不渲染,进入页面会有较长时间的空白内容

for (let index = 0; index < 5000000; index++) {
    document.write(' ')
}

我们可以将 utils.js 的引入放到内容渲染后面,改变任务的执行顺序,进入页面就不会有长时间的空白了

<body>
    <h1>JavaScript 的宏任务和微任务</h1>
    <script src="./utils.js"></script>
</body>

7. 任务共享内存

任务共享内存是指多个任务可以访问同一块内存区域

多个定时器设置相同时间延时执行,它们并不会同时执行,而是到达设定的时间后将它们放入任务队列,依次执行任务

let i = 0
setTimeout(() => {
    // 宏任务
    console.log(++i);
}, 1000)
setTimeout(() => {
    // 宏任务
    console.log(++i);
}, 1000)

8. 进度条实例体验任务轮询

先写好一个进度条的样式

<head>
    <style>
        .container {
            width: 200px;
            border-radius: 3px;
            border: 1px solid #ccc;
        }

        #progress {
            width: 0%;
            color: #fff;
            text-align: center;
            background-color: #376de1;
        }
    </style>
</head>

<body>
    <div class="container">
        <div id="progress">0</div>
    </div>
</body>

定时器其实就是往任务队列中不断的放入任务,到达执行时间后,交由主线程依次执行任务

function handle() {
    let i = 0;
    (function run() {
        if (++i <= 100) {
            progress.innerHTML = i
            progress.style.width = i + '%'
            setTimeout(run, 50)
        }
    })();
}
handle()

9. 任务拆分为多个子任务

现有以下代码,前面的累加函数需要较长时间才能执行完成,阻塞了后面代码的运行,我们来看一下怎么解决这个问题

let count = 0
let num = 987654321
function task() {
    for (let i = 0; i < num; i++) {
        count += num--
    }
    console.log(count);
}
task() // 同步任务
console.log('hello'); // 同步任务,需要等待 task() 执行完后才能执行

思路是将大量的计算放入异步任务,让同步任务先执行,以免造成阻塞

let count = 0
let num = 10000000
function task() {
    // 首次调用先累加一次
    for (let i = 0; i <= num; i++) {
        if (num <= 0) break;
        count += num--
    }
    if (num > 0) {
        // 后续累加,放入宏任务执行
        setTimeout(task)
    } else {
        console.log(count)
    }
}
task()
console.log('hello')

10. Promise 微任务处理复杂逻辑

将定时器放入 Promise 也可以处理复杂逻辑,不影响同步任务的执行,但下面用的还是宏任务(定时器)

let count = 0
let num = 987654321

// 由于 promise 里面的代码是同步执行的,在里面直接写循环还是会卡住,所以可以写个定时器
function task() {
    return new Promise(resolve => {
        let count = 0
        setTimeout(() => {
            for (let i = 0; i < num; i++) {
                count += num--
            }
            resolve(count)
        })
    });
}
async function getSum(num) {
    let res = await task(num)
    console.log(res);
}
getSum(num)
console.log('hello');

因为 Promise 的 then 回调方法是微任务,那么我们可以将代码调整为:

let num = 987654321
async function getSum(num) {
    let res = await Promise.resolve().then(_ => {
        let count = 0
        for (let i = 0; i < num; i++) {
            count += num--
        }
        return count
    })
}
getSum(num)
console.log('hello');

总结 

到此这篇关于JavaScript宏任务和微任务示例详解的文章就介绍到这了,更多相关JS宏任务和微任务内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • JavaScript正则表达式小结(test|match|search|replace|split|exec)

    JavaScript正则表达式小结(test|match|search|replace|split|exec)

    这篇文章主要介绍了JavaScript正则表达式小结(test|match|search|replace|split|exec)的相关资料,非常不错,具有参考借鉴价值,需要的朋友参考下吧
    2016-12-12
  • webpack打包时如何修改文件名的实现示例

    webpack打包时如何修改文件名的实现示例

    本文主要介绍了webpack打包时如何修改文件名的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-06-06
  • 如何使用50行javaScript代码实现简单版的call,apply,bind

    如何使用50行javaScript代码实现简单版的call,apply,bind

    这篇文章主要介绍了50行javaScript代码实现简单版的call,apply,bind过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-08-08
  • 微信小程序dom操作的替代思路实例分析

    微信小程序dom操作的替代思路实例分析

    这篇文章主要介绍了微信小程序dom操作的替代思路,结合实例形式分析了微信小程序通过事件响应、动态操作元素属性来实现dom操作效果,需要的朋友可以参考下
    2018-12-12
  • 基于JS实现右侧缓慢弹窗动态效果

    基于JS实现右侧缓慢弹窗动态效果

    在现代Web开发中,动态交互效果是提升用户体验的重要手段之一,其中,从页面右侧缓慢滑出的弹窗效果因其不遮挡主要内容、易于用户操作而备受欢迎,本文将详细介绍如何使用JavaScript结合CSS3动画实现这一效果,并探讨其在实际项目中的应用,需要的朋友可以参考下
    2025-02-02
  • checkbox勾选判断代码分析

    checkbox勾选判断代码分析

    我们在做项目的时候,特别是注册用户的时候,基本上都需要有注册协议之类的东西,那么我们如何判断用户是否勾选了同意了此协议呢?
    2014-06-06
  • JS兼容浏览器的导出Excel(CSV)文件的方法

    JS兼容浏览器的导出Excel(CSV)文件的方法

    项目中经常需要导出Excel文件,不在服务器端处理而是富客户端采用Javascript脚本处理数据并导出文件
    2014-05-05
  • 详解JS ES6编码规范

    详解JS ES6编码规范

    本章节讨论使用ES6编码风格到代码中需要注意的点。通过记住这些关键点,可以让我们写出更优美的、可读性更强的JavaScript ES6风格的代码。
    2021-05-05
  • 微信小程序实现随机验证码

    微信小程序实现随机验证码

    这篇文章主要为大家详细介绍了微信小程序实现随机验证码,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-05-05
  • js实现根据汉字的拼音按照a-z的方式进行排序

    js实现根据汉字的拼音按照a-z的方式进行排序

    这篇文章主要为大家详细介绍了如何使用JavaScript实现根据汉字的拼音按照a-z的方式进行排序,文中的示例代码讲解详细,需要的小伙伴可以了解下
    2025-08-08

最新评论