第108题 浏览器和nodejs事件循环(Event Loop)有什么区别
单线程和异步
- JS是单线程的,无论在浏览器还是在nodejs
- 浏览器中JS执行和DOM渲染共用一个线程,是互斥的
- 异步是单线程的解决方案
浏览器中的事件循环
异步里面分宏任务和微任务
- 宏任务:
setTimeout,setInterval,setImmediate,I/O,UI渲染,网络请求 - 微任务:
Promise,process.nextTick,MutationObserver、async/await - 宏任务和微任务的区别:微任务的优先级高于宏任务,微任务会在当前宏任务执行完毕后立即执行,而宏任务会在下一个事件循环中执行
- 宏任务在
页面渲染之后执行 - 微任务在
页面渲染之前执行 - 也就是微任务在下一轮
DOM渲染之前执行,宏任务在DOM渲染之后执行
- 宏任务在

console.log('start')
setTimeout(() => {
console.log('timeout')
})
Promise.resolve().then(() => {
console.log('promise then')
})
console.log('end')
// 输出
// start
// end
// promise then
// timeout
// 分析
// 等同步代码执行完后,先从微任务队列中获取(微任务队列优先级高),队列先进先出
// 宏任务 MarcoTask 队列
// 如setTimeout 1000ms到1000ms后才会放到队列中
const MarcoTaskQueue = [
() => {
console.log('timeout')
},
fn // ajax回调放到宏任务队列中等待
]
ajax(url, fn) // ajax 宏任务 如执行需要300ms
// ********** 宏任务和微任务中间隔着 【DOM 渲染】 ****************
// 微任务 MicroTask 队列
const MicroTaskQueue = [
() => {
console.log('promise then')
}
]
// 等宏任务和微任务执行完后 Event Loop 继续监听(一旦有任务到了宏任务微任务队列就会立马拿过来执行)...
<p>Event Loop</p>
<script>
const p = document.createElement('p')
p.innerHTML = 'new paragraph'
document.body.appendChild(p)
const list = document.getElementsByTagName('p')
console.log('length----', list.length) // 2
console.log('start')
// 宏任务在页面渲染之后执行
setTimeout(() => {
const list = document.getElementsByTagName('p')
console.log('length on timeout----', list.length) // 2
alert('阻塞 timeout') // 阻塞JS执行和渲染
})
// 微任务在页面渲染之前执行
Promise.resolve().then(() => {
const list = document.getElementsByTagName('p')
console.log('length on promise.then----', list.length) // 2
alert('阻塞 promise') // 阻塞JS执行和渲染
})
console.log('end')
</script>

nodejs中的事件循环
- nodejs也是单线程,也需要异步
- 异步任务也分为:宏任务 + 微任务
- 但是,它的宏任务和微任务分为不同的类型,有不同的优先级
- 和浏览器的主要区别就是
类型和优先级,理解了这里就理解了nodejs的事件循环
宏任务类型和优先级
类型分为6个,优先级从高到底执行
- Timer :
setTimeout、setInterval - I/O callbacks :处理网络、流、TCP的错误回调
- Idle,prepare :闲置状态(nodejs内部使用)
- Poll轮询 :执行
poll中的I/O队列 - Check检查 :存储
setImmediate回调 - Close callbacks :关闭回调,如
socket.on('close')
注意 :
process.nextTick优先级最高,setTimeout比setImmediate优先级高
执行过程
- 执行同步代码
- 执行微任务(
process.nextTick优先级最高) - 按顺序执行6个类型的宏任务(每个开始之前都执行当前的微任务)

总结
- 浏览器和nodejs的事件循环流程基本相同
- nodejs宏任务和微任务分类型,有优先级。浏览器里面的宏任务和微任务是没有类型和优先级的
- node17之后推荐使用
setImmediate代替process.nextTick(如果使用process.nextTick执行复杂任务导致后面的卡顿就得不偿失了,尽量使用低优先级的api去执行异步)
console.info('start')
setImmediate(() => {
console.info('setImmediate')
})
setTimeout(() => {
console.info('timeout')
})
Promise.resolve().then(() => {
console.info('promise then')
})
process.nextTick(() => {
console.info('nextTick')
})
console.info('end')
// 输出
// start
// end
// nextTick
// promise then
// timeout
// setImmediate
