前端 - JavaScript - - 宏任务&微任务详解

发布于:2025-07-20 ⋅ 阅读:(12) ⋅ 点赞:(0)

在javascript的事件循环(Event Loop)中,将任务分为两种:宏任务与微任务,掌握宏任务与微任务的执行原理,可大幅度提升前端页面渲染性能

1、宏任务

宏任务是浏览器环境或nodejs环境提供的任务,通常是一些离散的、独立的工作单元,如:

script整体代码、setTimeout、setInterval、setImmedite(仅nodejs)、UI渲染

2、微任务

微任务是javascript内部提供的任务,是相比于宏任务更小的工作单元,如:

Promise.then/catch/finally、mutationobserve、process.nexttick(仅nodejs 严格意义上不属于微任务,但是有 比微任务更高的 优先级)

3、执行顺序(优先级从高到低)

1.process.nexttick

2.当前宏任务

3.微任务

首先是process.nexttick的优先级最高,其次是执行当前宏任务队列,当前宏任务执行后会立刻执行微任务队列,然后再执行下一个宏任务

4、场景题

console.log(1);

setTimeout(()=>{
    console.log(2)
});

new Promise((resolve)=>{
    console.log(3);
    resolve();
}).then(()=>{
    console.log(4);
});

console.log(5);

执行顺序:1、3、5、4、2

解析:

1. console.log属于同步任务 promise构造函数中的回调函数(executor函数)(resolve,reject)=>{} 也属于同步任务 所以这三个同步任务会依次优先执行 输出1、3、5

2. promise.then属于微任务,所以在执行完当前宏任务后 立即执行当前微任务 输出4

3. setTimeout属于宏任务,那为什么不是先执行宏任务的console.log(2)呢? 因为setTimeout在当前宏任务队列中,它会把自己的回调函数推到下一个宏任务队列 所以最后输出2

setTimeout(()=>{
    console.log(1);
    new Promise(()=>{
        console.log(2);
    }).then(()=>{
        console.log(3);
    })
});

new Promise((resolve)=>{
    setTimeout(()=>{
        console.log(4);
    });
    console.log(5);
    resolve()
}).then(()=>{
    console.log(6)
}).then(()=>{
    console.log(7)
});

console.log(8)

执行顺序:5、8、6、7、1、2、4

解析:(注意空格断句~)

1. 从上往下依次执行同步任务 输出5、8

2. 第二个promise的第一个.then会把回调函数放入微任务队列 待宏任务执行后立即执行 输出6,第一个.then执行后会执行第二个.then 将回调函数放入微任务队列 输出7,此时微任务队列已被清空

3. 第一个setTimeout在第一个宏任务队列中 将回调函数放入第二个宏任务队列,此时回调函数内部皆为同步任务,从上往下依次输出1、2,那3呢?因为改promise中并未执行resolve()(成功的回调)所以.then不生效

4. console.log(4)的setTimeout是在 第一个宏任务之后的 微任务队列中,被放入第三个宏任务队列,所以当第二个宏任务队列清空后,执行第三个宏任务队列,输出4

5、通过宏任务与微任务机制优化代码执行性能

1. 将非关键性任务推迟到下一个宏任务队列 让主线程先处理关键任务

const fn = () => {
    // 非关键性功能逻辑
    // ...
};

setTimeout(()=>{
    fn();
});

2. 将多个UI更新任务一起汇聚到微任务队列 同时更新dom 减少重绘次数

<div>{{text}}</div>
<div>{{count}}</div>
<img :src='imgUrl'>

let text = '我是老text';
let count = 1;
let imgUrl = 'xxx';

Promise.resolve().then(()=>{
    text = '我是新text';
    count = 2;
    imgUrl = 'https://xxx'
})

3. 切片异步处理数组循环任务 将长数组分成多个短数组 异步执行任务 避免浏览器卡顿

const myArr = [...] // 被执行的数组

const fn = (arr) => {
    arr.forEach((ele)=>{
        // 处理逻辑
        // ...
    });
};

const step = 5; // 将数组分为若干个长度为5的小数组
let index = 0; // 数组从索引为index的位置开始截取

const recursionFn = (arr) => {
    // 截取获取新的小数组
    const newArr = arr.splice(index,step);
    // 将新的小数组传入处理函数中
    fn(newArr);
    // 如果旧数组的长度依然比新数组的长度长,则先将index处理成下一个新数组 截取的初始位置,然后递归执行此函数
    if(newArr.length < arr.length) {
        index += step
        recursionFn(newArr)
    }
}

recursionFn(myArr)



网站公告

今日签到

点亮在社区的每一天
去签到