news 2026/7/21 15:58:13

Event Loop事件循环

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Event Loop事件循环

1. 同步和异步

JavaScript是一种单线程语言,所有任务都在一个线程上完成。一旦遇到大量任务或者遇到一个耗时的任务,比如加载一个高清图片,网页就会出现"假死",因为JavaScript停不下来,也就无法响应用户的行为。为了防止主线程的阻塞,JavaScript 有了 同步 和 异步 的概念。

同步:调用一旦开始,调用者必须等待方法执行完成,才能继续执行后续方法。
异步:方法一旦开始,立即返回,调用者无需等待其中方法执行完成,就可以继续执行后续方法。

2. 事件循环机制 Event Loop

事件循环(Event Loop)是 JavaScript 运行时(浏览器/Node.js)用于协调同步代码、异步回调、页面渲染的一套调度机制。它决定了代码执行的顺序和时机

2.1 执行顺序

  1. 先执行一个宏任务也就是整体script里的代码
  2. 遇到同步任务立即执行
  3. 遇到宏任务则将宏任务放入宏任务队列中
  4. 遇到微任务则将微任务放入微任务队列中
  5. 当所有同步代码执行完毕后,再将微任务从队列中调入主线程执行直至微任务队列清空(哪怕执行微任务时又产生了新的微任务,也会在本轮继续清空,直到微任务队列为空,才会取下一个宏任务。)
  6. 检查是否需要渲染页面,如果需要渲染 → 执行渲染流程(样式计算 → 布局 → 绘制);如果不需要 → 跳过
  7. 再从宏任务队列中取出一个执行
  8. 一直循环直至所有任务执行完毕

2.2 宏任务

宏任务来源说明示例
整个script标签的执行事件循环启动后取出的第一个宏任务,所有全局同步代码都是它的执行体页面加载时执行的JS代码
setTimeout/setInterval回调定时器延迟时间达到后,回调被加入宏任务队列setTimeout(()=>{}, 0)
UI交互事件回调用户操作触发后,回调加入宏任务队列click/keydown/mousemove/touchstart等
I/O完成回调浏览器底层I/O操作完成后触发的回调(注意:不是JS回调本身)fetch/XHR网络请求完成、FileReader文件读取完成、IndexedDB操作完成
postMessage回调跨窗口/跨Worker通信时,消息被接收后触发的回调window.postMessage(data)的onmessage回调
MessageChannel回调通过MessageChannel创建的端口发送消息后,接收端的onmessage回调const channel = new MessageChannel(); channel.port1.onmessage = () => {}
requestAnimationFrame回调浏览器下一次渲染前执行的回调,属于宏任务,但优先级高于普通宏任务做动画时常用的API
requestIdleCallback回调浏览器空闲时期执行的回调,优先级低于普通宏任务,只有主线程无任务时才会执行执行低优先级任务(如日志上报、预加载)

2.2 微任务

微任务来源说明你熟悉的场景
Promise.prototype.then/catch/finally回调最常见的微任务,Promise状态变更后触发你写异步逻辑最常用的API
await后面的代码await xx等价于 Promise.resolve(xx).then(()=>{}),await后的代码就是then的回调,属于微任务await后面的语句不会阻塞同步代码
queueMicrotask回调ES2020新增的API,专门用于创建微任务,比Promise.then更纯粹(没有Promise状态转换的开销)Vue3的nextTick底层就用它兜底
MutationObserver回调监听DOM变化触发的回调,DOM变更后、下一个宏任务前执行Vue2的nextTick在兼容环境下用它实现
IntersectionObserver回调监听元素可见性变化触发的回调,属于微任务图片懒加载、曝光埋点的核心API

2.4 例题

setTimeout(function(){ console.log('1'); }); new Promise(function(resolve){ console.log('2'); resolve(); }).then(function(){ console.log('3'); }); console.log('4'); //输出顺序:2 4 3 1

解释:

  1. 遇到setTimout,异步宏任务,放入宏任务队列中
  2. 遇到new Promise,Promise在实例化的过程中所执行的代码都是同步进行的,所以输出2
  3. 而Promise.then中注册的回调才是异步执行的,将其放入微任务队列中
  4. 遇到同步任务console.log(‘4’);输出4;主线程中同步任务执行完
  5. 从微任务队列中取出任务到主线程中,输出3,微任务队列为空
  6. 从宏任务队列中取出任务到主线程中,输出1,宏任务队列为空,结束
setTimeout(()=>{ new Promise(resolve =>{ resolve(); }).then(()=>{ console.log('test'); }); console.log(4); }); new Promise(resolve => { resolve(); console.log(1) }).then( () => { console.log(3); Promise.resolve().then(() => { console.log('before timeout'); }).then(() => { Promise.resolve().then(() => { console.log('also before timeout') }) }) }) console.log(2); //输出:1 2 3 before timeout also before timeout 4 test

解释:

  1. 遇到setTimeout,异步宏任务,将() => {console.log(4)}放入宏任务队列中
  2. 遇到new Promise,Promise在实例化的过程中所执行的代码都是同步进行的,所以输出1;
  3. 而Promise.then中注册的回调才是异步执行的,将其放入微任务队列中
  4. 遇到同步任务console.log(2),输出2;主线程中同步任务执行完
  5. 从微任务队列中取出任务到主线程中,输出3,此微任务中又有微任务,Promise.resolve().then(微任务a).then(微任务b),将其依次放入微任务队列中;
  6. 从微任务队列中取出任务a到主线程中,输出 before timeout;
  7. 从微任务队列中取出任务b到主线程中,任务b又注册了一个微任务c,放入微任务队列中;
  8. 从微任务队列中取出任务c到主线程中,输出 also before timeout;微任务队列为空
  9. 从宏任务队列中取出任务到主线程,此任务中注册了一个微任务d,将其放入微任务队列中,接下来遇到输出4,宏任务队列为空
  10. 从微任务队列中取出任务d到主线程 ,输出test,微任务队列为空,结束
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/21 15:57:47

AI4R核心功能大揭秘:从分类器到Transformer的全方位解析

AI4R核心功能大揭秘:从分类器到Transformer的全方位解析 【免费下载链接】ai4r Artificial Intelligence for Ruby - A Ruby playground for AI researchers 项目地址: https://gitcode.com/gh_mirrors/ai/ai4r AI4R(Artificial Intelligence for…

作者头像 李华
网站建设 2026/7/21 15:57:23

计算机毕业设计之网上书店系统

随着新经济的需求和新技术的发展,特别是网络技术的发展,如果可以建立起网上书店系统,可以改变传统线下管理方式,在过去的时代里都使用传统的方式实行,既花费了时间,又浪费了精力。在信息如此发达的今天&…

作者头像 李华
网站建设 2026/7/21 15:56:22

java汉诺塔(递归实现)

目录 1.1 汉诺塔介绍 1.2 游戏 1.3 汉诺塔实现步骤 1.4 实现代码 测试结果 1.1 汉诺塔介绍 汉诺塔介绍 1.2 游戏 汉诺塔小游戏 1.3 汉诺塔实现步骤 设置3个柱子,初始柱子(A),中间辅助柱子(B)&am…

作者头像 李华
网站建设 2026/7/21 15:54:21

一款基于车载协议的socket通讯工具

下载地址:CetNetworkTester-Setup-Beta-V1.1.2.111.exe-互联网文档类资源-CSDN下载 汽车协议网络测试工具:CetNetworkTester 设计笔记 作者:CetXiyuan | 技术栈:Qt 5.15.2 (MinGW 32-bit) | 平台&#xff…

作者头像 李华
网站建设 2026/7/21 15:53:58

面向对象的思考

1. 封装 1.1 概念 与 意义 到底是为了安全 还是为了对用户友好? 1.2 封装是指 功能的打包和接口的暴漏 把实现的细节隐藏在接口之后并不代表必须有强制的私有性 先保护好自己再让别人利用,这两者并不冲突 ,让能看到的看到,不能…

作者头像 李华