宏任务和微任务
5/10/2021 js基础
# 什么是事件循环
JS 引擎是单线程的。JS做的任务分为同步和异步两种,所谓 "异步",简单说就是一个任务不是连续完成的,先执行第一段,等做好了准备,再回过头执行第二段,第二段也被叫做回调;同步则是连贯完成的。
如果没有异步操作,在碰到一些花费时间很长的操作时,JS这种这种单线程语言会等待结果,这种长时间空闲等待是不可接受的,所以JS采用了“异步任务回调”的模式。
在等待异步任务准备的同时,JS引擎去执行其他同步任务,等到异步任务准备好了,再去执行回调。这种模式的优势显而易见,完成相同的任务,花费的时间大大减少,这种方式也被叫做非阻塞式。
而实现这个的,正是事件循环。
事件循环是由一个队列组成的,异步任务的回调遵循先进先出,在 JS 引擎空闲时会一轮一轮地被取出,所以被叫做循环。
根据队列中任务的不同,分为宏任务和微任务。
# 宏任务和微任务
事件循环由宏任务和微任务组成,完成当下的宏任务后,会执行在此期间入队的微任务。
常见的宏任务有: script/setTimout/setInterval/requestAnimationFrame/UI render
常见的微任务有:Promise.then()/Object.observe/process.nextTick(node 独有)
# 聊一聊浏览器的事件循环
浏览器首先执行宏任务:script脚本。碰到宏任务和微任务时加入各自队列中,在script执行完成后,执行当前微任务队列。这是一次事件循环。
然后再取出一个宏任务,再将碰到的任务加入队列,然后再清空当前微任务队列。
...
需要注意的是每个宏任务都有自己的微任务队列
。每次都是一个宏任务执行完,再执行当前宏任务下的微任务队列。
经典例子:
console.log('script1')
Promise.resolve().then(()=>{
console.log('第一个回调函数:微任务1')
setTimeout(()=>{
console.log('第三个回调函数:宏任务2')
},0)
})
console.log('script2')
setTimeout(()=>{
console.log('第二个回调函数:宏任务1')
Promise.resolve().then(()=>{
console.log('第四个回调函数:微任务2')
})
},0)
console.log('script3')
大致流程梳理如下,这个地方需要自己认真理解。
-> script开始执行 -> promise加入微任务1队列 -> settimeout加入新宏任务1
-> script执行完毕 -> 执行promise微任务1队列 -> settimeout加入新宏任务2 -> 微任务1队列执行完毕
-> 执行宏任务1 -> promise加入微任务2队列 -> 宏任务1执行完毕 -> 执行微任务2队列 -> 微任务2队列执行完毕
-> 执行宏任务2 -> 宏任务2执行完毕 -> over