Javascript中的事件循环是如何工作的_如何利用Javascript事件循环优化异步代码?

发布时间 - 2025-12-31 00:00:00    点击率:
JavaScript事件循环中,宏任务执行完后必须清空整个微任务队列才进入下一轮宏任务;queueMicrotask比Promise.then更轻量且语义明确,适用于同步代码后立即异步执行,但不触发渲染;requestAnimationFrame才是读取布局信息的正确时机。

JavaScript事件循环的核心机制:宏任务和微任务的调度顺序

JavaScript是单线程语言,事件循环(Event Loop)不是“轮询”,而是按严格优先级分阶段执行任务。关键在于区分两类队列:macrotask(如 setTimeoutsetInterval、I/O、UI渲染)和 microtask(如 Promise.thenqueueMicrotaskMutationObserver)。每次宏任务执行完后,**必须清空整个微任务队列**,才会进入下一轮宏任务。

这意味着:

  • Promise.resolve().then() 总比 setTimeout(() => {}, 0) 先执行,哪怕后者时间设为 0
  • queueMicrotaskPromise.then 在同一轮微任务中,但前者不创建 Promise 对象,开销略小
  • 浏览器在每次宏任务(如一次点击回调)结束后,会强制进行一次 UI 渲染(除非被 requestIdleCallback 或节流干预)

queueMicrotask 替代 setTimeout(..., 0) 做异步“让出”控制权

当你需要把一段逻辑推迟到当前同步代码之后、但又不想等到下一个宏任务周期(比如避免 UI 卡顿或保证 DOM 更新可见),queueMicrotask 是更精准的选择。它比 Promise.then 更轻量,且语义更明确——就是“等这次同步栈清空后立刻执行”。

function renderLargeList(items) {
  const container = document.getElementById('list');
  items.forEach((item, i) => {
    const el = document.createElement('div');
    el.textContent = item;
    container.appendChild(el);

    // 每处理 20 项,让出主线程,避免阻塞渲染
    if ((i + 1) % 20 === 0) {
      queueMicrotask(() => {});
    }
  });
}

注意:queueMicrotask(() => {}) 不会立即触发重排/重绘,但它确保了浏览器有机会在下一批元素插入前完成上一批的样式计算与布局——这比 setTimeout(..., 0) 更可控。

立即学习“Java免费学习笔记(深入)”;

常见误用:

  • 在循环中反复调用 queueMicrotask 处理大量数据 → 微任务队列爆炸,反而卡死主线程
  • 期望 queueMicrotask 触发 DOM 渲染 → 它不触发渲染,渲染只发生在宏任务之间

避免微任务“无限嵌套”导致的饥饿问题

微任务会在每次宏任务后被清空,但如果每个微任务又自己调度一个新的微任务,就可能形成无限链,阻塞后续宏任务(包括用户输入、定时器、网络响应),表现为页面完全无响应。

let count = 0;
function runaway() {
  if (count < 1000) {
    count++;
    queueMicrotask(runaway); // ❌ 错误:没有中断条件或延迟
  }
}
queueMicrotask(runaway);

正确做法是引入节流或降级为宏任务:

  • setTimeout 控制节奏(每轮只处理一部分)
  • 结合 requestIdleCallback 在浏览器空闲时处理(兼容性需检查)
  • 对高频率触发的逻辑(如输入框实时校验),用防抖或 input 事件 + queueMicrotask 组合,而非在每次输入都推微任务

异步代码优化的关键判断点:你到底需要“稍后”还是“下一帧”?

很多性能问题源于混淆了时间粒度:

  • “稍后执行”(不阻塞当前逻辑,但不必等待渲染)→ 用 queueMicrotaskPromise.resolve().then()
  • “下一帧开始前执行”(确保 DOM 已更新、样式已计算,再读取 offsetHeight 等)→ 用 requestAnimationFrame
  • “下一帧渲染后执行”(比如动画结束后的清理)→ 用 requestAnimationFrame + setTimeoutPromise 配合 getComputedStyle 检测

例如,要安全读取刚插入 DOM 的元素尺寸:

el.style.display = 'block';
document.body.appendChild(el);
// ✅ 正确:等样式应用 + 布局完成后再读
requestAnimationFrame(() => {
  requestAnimationFrame(() => {
    console.log(el.offsetHeight); // 此时 layout 已触发
  });
});

微任务无法保证 layout 完成;requestAnimationFrame 回调在浏览器 layout 之后、paint 之前执行,这才是真正“下一帧”的锚点。

最容易被忽略的是:即使用了 await Promise.resolve(),也不能替代 requestAnimationFrame 来读取布局信息——因为 Promise 不参与渲染管线。


# javascript  # java  # 浏览器  # app  # mac  #   # ai  # 重绘 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: 使用spring连接及操作mongodb3.0实例  Android 常见的图片加载框架详细介绍  UC浏览器如何切换小说阅读源_UC浏览器阅读源切换【方法】  jQuery validate插件功能与用法详解  Windows10电脑怎么设置虚拟光驱_Win10右键装载ISO镜像文件  Laravel中的Facade(门面)到底是什么原理  今日头条微视频如何找选题 今日头条微视频找选题技巧【指南】  微信小程序 闭包写法详细介绍  公司门户网站制作公司有哪些,怎样使用wordpress制作一个企业网站?  如何挑选最适合建站的高性能VPS主机?  Google浏览器为什么这么卡 Google浏览器提速优化设置步骤【方法】  香港服务器网站测试全流程:性能评估、SEO加载与移动适配优化  开心动漫网站制作软件下载,十分开心动画为何停播?  微信h5制作网站有哪些,免费微信H5页面制作工具?  如何批量查询域名的建站时间记录?  Python数据仓库与ETL构建实战_Airflow调度流程详解  zabbix利用python脚本发送报警邮件的方法  网站制作壁纸教程视频,电脑壁纸网站?  Laravel怎么返回JSON格式数据_Laravel API资源Response响应格式化【技巧】  瓜子二手车官方网站在线入口 瓜子二手车网页版官网通道入口  b2c电商网站制作流程,b2c水平综合的电商平台?  如何在Tomcat中配置并部署网站项目?  Laravel怎么实现前端Toast弹窗提示_Laravel Session闪存数据Flash传递给前端【方法】  Laravel的路由模型绑定怎么用_Laravel Route Model Binding简化控制器逻辑  ai格式如何转html_将AI设计稿转换为HTML页面流程【页面】  Win11关机界面怎么改_Win11自定义关机画面设置【工具】  在centOS 7安装mysql 5.7的详细教程  Laravel storage目录权限问题_Laravel文件写入权限设置  Laravel如何使用Eloquent进行子查询  iOS UIView常见属性方法小结  电商网站制作多少钱一个,电子商务公司的网站制作费用计入什么科目?  Laravel如何清理系统缓存命令_Laravel清除路由配置及视图缓存的方法【总结】  如何挑选优质建站一级代理提升网站排名?  Laravel如何集成第三方登录_Laravel Socialite实现微信QQ微博登录  Laravel Debugbar怎么安装_Laravel调试工具栏配置指南  Laravel如何实现多表关联模型定义_Laravel多对多关系及中间表数据存取【方法】  Java垃圾回收器的方法和原理总结  香港服务器建站指南:外贸独立站搭建与跨境电商配置流程  小视频制作网站有哪些,有什么看国内小视频的网站,求推荐?  微信小程序 配置文件详细介绍  Laravel如何实现API版本控制_Laravel版本化API设计方案  使用Dockerfile构建java web环境  大连企业网站制作公司,大连2025企业社保缴费网上缴费流程?  七夕网站制作视频,七夕大促活动怎么报名?  米侠浏览器网页图片不显示怎么办 米侠图片加载修复  HTML透明颜色代码怎么让下拉菜单透明_下拉菜单透明背景指南【技巧】  详解jQuery停止动画——stop()方法的使用  利用JavaScript实现拖拽改变元素大小  安克发布新款氮化镓充电宝:体积缩小 30%,支持 200W 输出  Edge浏览器如何截图和滚动截图_微软Edge网页捕获功能使用教程【技巧】