宏任务与微任务
原文:做一些动图,学习一下EventLoop (https://juejin.cn/post/6969028296893792286)
一、任务队列
JavaScript 是单线程执行的语言, 在同一时间只能干一件事情。如果前面的任务很耗时后面的任务就会一直等待,为了解决这个问题,js中出现了同步任务和异步任务
1.1 同步任务
在主线程上排队执行的任务只有前面执行完毕才能执行下一个任务,形成一个执行栈
1.2 异步任务
不进入主线程,而是进入任务队列,只有当主线程的任务执行完毕,才会从任务队列中拿任务到主线程来执行。由于主线程不断循环 获取任务 -> 执行任务 -> 再次获取任务 -> 再次执行任务
,所以这种机制被叫做 事件循环
。
二、宏任务和微任务
在任务队列中其实还分为宏任务队列和微任务队列,里面存放的就是宏任务和微任务
宏任务和微任务都是异步任务, 这两者的区别就是它们的执行顺序。
在同步任务中, 任务都是讲究按照代码顺序执行的, 而异步任务也是需要按照顺序执行的; 队列的属性就说先进先出, 因此异步任务会按照进入队列的顺序以此执行。
这就解释了为什么下列代码中,为什么后面的定时器比前面的定时器先执行。因为后者的定时器先推进宏任务队列,而前者会在到时间后再被推进宏任务队列
setTimeout(() => { console.log('a'); }, 10000); setTimeout(() => { console.log('b'); }, 100);
2.1 宏任务
宏任务包括:script(整体代码)
, setTimeout
, setInterval
, Ajax
, DOM事件
, requestAnimationFrame ( 只存在浏览器中 )
, I/O
, SetImmediate( node )
2.2 微任务
微任务包括:Promise.then
, async/await
, Object.observe ( 已废弃 )
, MutationObserver ( HTML5新特性, 只存在浏览器中 )
, process.nextTick ( node )
三、完整的执行顺序
- 从上往下执行所有的同步代码
- 在执行过程中遇到宏任务就存放到宏任务队列中, 遇到微任务就存放到微任务队列中
- 当所有的同步任务执行完毕, 就执行微任务队列中满足需求的所有回调( process.nextTick 优先 )
- 当微任务队列中所有满足需求的回调执行完毕后, 就执行宏任务队列中满足需求的所有回调 ( SetImmediate 最后 )
3.1 注意
- 每执行完一个宏任务都会立即检查微任务列表有没有清空, 如果没有就立即清空。
- Promise里的是同步任务,立即执行
- Promise.then() 中如果不写函数的话也是同步任务
四、练习题
4.1 第一题
console.log(100) setTimeout(() => { console.log(200) }) Promise.resolve().then(() => { console.log(300) }) console.log(400) // 100、400、300、200 // 1. 第一遍执行同步任务输出 100、400 // 2. 遇到 setTimeout 将其加入宏任务队列、Promise.then 将其加入微任务队列 // 3. 先执行微任务输出 300 // 4. 再执行宏任务输出 200
4.2 第二题
setTimeout(function() { console.log('a') }); new Promise(function(resolve) { console.log('b'); for (var i = 0; i < 10000; i++) { i == 99 && resolve(); } }).then(function() { console.log('c') }); console.log('d'); // b、d、c、a // 1. 先执行同步任务, 输出b、d ( 注意:Promise里的是同步任务 ) // 2. 遇到 setTimeout 将其加入宏任务队列、Promise.then 将其加入微任务队列 // 3. 先执行微任务输出 c // 4. 再执行宏任务输出 a
4.3 第三题
let promise = new Promise((resolve) => { setTimeout(() => { console.log('a') resolve() }, 0) console.log('b') }).then(value => console.log('c')) console.log('d') // b、d、a、c // 解析: // 读取到第 2 行, 将 setTimeout 加入宏任务队列 // 读取到第 6 行,执行输出 b // 读取到第 7 行,因为还没有执行 resolve() 所以暂时不会创建 Promise.then // 读取到第 9 行,执行输出 d // 由于没有微任务,开始执行宏任务 // 读取 setTimeout 的代码, 输出 a, 并且创建 Promise.then // 执行微任务,输出 c
4.4 第四题
console.log('a'); setTimeout(function() { console.log('b'); process.nextTick(function() { console.log('c'); }) new Promise(function(resolve) { console.log('d'); resolve(); }).then(function() { console.log('e') }) }) process.nextTick(function() { console.log('f'); }) new Promise(function(resolve) { console.log('g'); resolve(); }).then(function() { console.log('h') }) setTimeout(function() { console.log('i'); process.nextTick(function() { console.log('j'); }) new Promise(function(resolve) { console.log('k'); resolve(); }).then(function() { console.log('l') }) }) // 第一轮 // 1.1. 进入主线程,遇到 console.log('a'), 执行打印 a // 1.2. 遇到 setTimeout 将其加入到 宏任务队列 // 1.3. 遇到 process.nextTick 将其加入到微任务队列 // 1.4. 遇到 Promise 将同步任务 g 打印 // 1.5. 将 Promise.then 加入到微任务队列 // 1.6. 遇到第二个 setTimeout 将其加入到 宏任务队列 // 打印情况: a、g // 宏任务( 由底到高 ):setTimeout (P3)、setTimeout (P26) // 微任务( 由底到高 ):process.nextTick (P16)、Promise.then (P22) // 1.7 查找微任务队列,由下开始执行 // 1.8 执行 process.nextTick, 打印 f // 1.9 执行 Promise.then, 打印 h // 打印情况: a、g、f、h // 第二轮 // 2.1 从宏任务队列底部开始执行 setTimeout (P3) // 2.2 遇到 console.log('b') 打印 b // 2.3 遇到 process.nextTick 加入到微任务队列 // 2.4 遇到 Promise 将同步任务 d 打印 // 2.5 将 Promise.then 加入到微任务队列 // 打印情况: a、g、f、h、b、d // 宏任务( 由底到高 ):setTimeout (P26) // 微任务( 由底到高 ):process.nextTick (P5)、Promise.then (P11) // 2.6 查找微任务队列,由下开始执行 // 2.7 执行 process.nextTick, 打印 c // 2.8 执行 Promise.then, 打印 e // 打印情况: a、g、f、h、b、d、c、e // 第三轮 // 3.1 从宏任务队列底部开始执行 setTimeout (P26) // 3.2 遇到 console.log('i') 打印 i // 3.3 遇到 process.nextTick 加入到微任务队列 // 3.4 遇到 Promise 将同步任务 k 打印 // 3.5 将 Promise.then 加入到微任务队列 // 打印情况: a、g、f、h、b、d、c、e、i、k // 宏任务( 由底到高 ):空 // 微任务( 由底到高 ):process.nextTick (P28)、Promise.then (P35) // 3.6 查找微任务队列,由下开始执行 // 3.7 执行 process.nextTick, 打印 j // 3.8 执行 Promise.then, 打印 l // 打印情况: a、g、f、h、b、d、c、e、i、k、j、l
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· AI与.NET技术实操系列(六):基于图像分类模型对图像进行分类