快速了解浏览器事件循环Event Loop

本文介绍了浏览器中的事件循环机制,阐述了JS单线程运行的特点,详细讲解了执行栈、任务队列、宏任务与微任务的概念,并探讨了DOM渲染与事件循环的关系。在每个事件循环中,主线程先执行宏任务,接着处理微任务,直至队列为空。最后通过例题帮助读者巩固理解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

背景

首先我们知道JS是单线程运行的,也就是每次只能执行一个任务。那么为了协调事件,用户交互,脚本,渲染,网络任务等,必须使用事件循环进行协调

事件循环机制有浏览器和Node两种实现,这里暂不讨论Node部分

基础知识

  • 是一种先进后出的数据结构
  • 队列 是一种先进先出的数据结构

JS运行机制

  1. 首先任务一个一个地进入主线程执行,形成一个执行栈(stack)
  2. 同步任务会立刻执行,异步任务会记录下,等待回调函数执行的时机
  3. 当回调函数需要触发了,就把回调函数放进任务队列(queue)
  4. 当执行栈里的任务全部执行完毕,主线程空闲,就回去读取任务队列里的任务,进入主线程执行
  5. 如此又回到了步骤1,循环这样一个“读取——执行”的过程

宏任务和微任务

上面的运行机制是简化了的,实际上任务可以细分为宏任务和微任务。上面所说的步骤2实际上会区分宏任务和微任务,分别放入不同的队列。

  • 宏任务script(整体代码), setTimeout, setInterval, I/O, UI Rendering, setImmediate(IE10和Node独有)
  • 微任务Promise(async/await), Object.observe(废弃), MutationObserver, Process.nextTick(Node独有)

Event Loop的完整过程

  1. 第一次事件循环,整体代码作为一个宏任务,进入执行栈
  2. 执行过程中,同步代码立刻执行,需要等待的任务根据宏任务和微任务,把回调函数分别放入两个队列
  3. 当主线程空闲的时候,首先检查微任务队列是否有任务,如果有的话,把微任务队列全部执行,如果在微任务的执行中又加入了新的微任务,那么循环检查并执行,直到队列为空才结束
  4. 然后再从宏任务队列中取出一个任务,进入执行栈
  5. 如此又回到了步骤1进行循环

补充

DOM渲染在什么时候

首先回忆上面提到的微任务MutationObserver

最初的页面是没有提供对DOM变化的监听的,不能实时感知到DOM的变化,这个接口的引入是为了监视DOM树发生变化并作出响应。当DOM发生变化的时候,生成一个微任务,推入队列中。

那么每次event loop结束都会进行页面渲染吗?

不一定,这个需要根据屏幕刷新率、页面性能、页面是否在后台运行等多个条件综合决定。一般来说这个渲染频率是固定的。

如果两个宏任务间隔时间很短,比如都是setTimeout(func, 0)那么只会触发一次渲染

如果两个宏任务间隔稍长,那么是会触发多次渲染的。

例题

new Promise(resolve => {
  console.log(1) // 这里需要注意
  resolve()
}).then(() => {
  console.log(2)
})

console.log(3)

setTimeout(() => {
  console.log(4)
})

打印结果是:1 3 2 4

需要注意的是我们在说加入任务队列的时候,都是把回调函数加入队列。不要看到Promise就觉得所有的内容都扔到队列里去了,new Promise的时候会执行传入的这个函数参数,console.log(1)是同步的,会立刻执行

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值