> 技术文档 > 前端 - JavaScript - - 宏任务&微任务详解_js 微任务

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

在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 减少重绘次数

{{text}}
{{count}}
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)