Javascript中的事件循环是如何工作的_如何利用Javascript事件循环优化异步代码?
发布时间 - 2025-12-31 00:00:00 点击率:次JavaScript事件循环中,宏任务执行完后必须清空整个微任务队列才进入下一轮宏任务;queueMicrotask比Promise.then更轻量且语义明确,适用于同步代码后立即异步执行,但不触发渲染;requestAnimationFrame才是读取布局信息的正确时机。
JavaScript事件循环的核心机制:宏任务和微任务的调度顺序
JavaScript是单线程语言,事件循环(Event Loop)不是“轮询”,而是按严格优先级分阶段执行任务。关键在于区分两类队列:macrotask(如 setTimeout、setInterval、I/O、UI渲染)和 microtask(如 Promise.then、queueMicrotask、MutationObserver)。每次宏任务执行完后,**必须清空整个微任务队列**,才会进入下一轮宏任务。
这意味着:
-
Promise.resolve().then()总比setTimeout(() => {}, 0)先执行,哪怕后者时间设为 0 -
queueMicrotask和Promise.then在同一轮微任务中,但前者不创建 Promise 对象,开销略小 -
浏览器在每次宏任务(如一次点击回调)结束后,会强制进行一次 UI 渲染(除非被
requestIdleCallback或节流干预)
用 queueMicrotask 替代 setTimeout(..
., 0) 做异步“让出”控制权
., 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组合,而非在每次输入都推微任务
异步代码优化的关键判断点:你到底需要“稍后”还是“下一帧”?
很多性能问题源于混淆了时间粒度:
- “稍后执行”(不阻塞当前逻辑,但不必等待渲染)→ 用
queueMicrotask或Promise.resolve().then() - “下一帧开始前执行”(确保 DOM 已更新、样式已计算,再读取
offsetHeight等)→ 用requestAnimationFrame - “下一帧渲染后执行”(比如动画结束后的清理)→ 用
requestAnimationFrame+setTimeout或Promise配合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 】
相关推荐:
Laravel Eloquent:优雅地将关联模型字段扁平化到主模型中
Laravel如何使用Contracts(契约)进行编程_Laravel契约接口与依赖反转
美食网站链接制作教程视频,哪个教做美食的网站比较专业点?
Laravel路由Route怎么设置_Laravel基础路由定义与参数传递规则【详解】
🚀拖拽式CMS建站能否实现高效与个性化并存?
什么是JavaScript解构赋值_解构赋值有哪些实用技巧
如何用AWS免费套餐快速搭建高效网站?
网页制作模板网站推荐,网页设计海报之类的素材哪里好?
谷歌Google入口永久地址_Google搜索引擎官网首页永久入口
网站制作价目表怎么做,珍爱网婚介费用多少?
如何基于PHP生成高效IDC网络公司建站源码?
公司门户网站制作公司有哪些,怎样使用wordpress制作一个企业网站?
Laravel如何使用模型观察者?(Observer代码示例)
CSS3怎么给轮播图加过渡动画_transition加transform实现【技巧】
Laravel如何使用软删除(Soft Deletes)功能_Eloquent软删除与数据恢复方法
Laravel如何实现多语言支持_Laravel本地化与国际化(i18n)配置教程
Laravel怎么创建控制器Controller_Laravel路由绑定与控制器逻辑编写【指南】
Laravel中间件起什么作用_Laravel Middleware请求生命周期与自定义详解
如何在局域网内绑定自建网站域名?
Laravel队列任务超时怎么办_Laravel Queue Timeout设置详解
Python自然语言搜索引擎项目教程_倒排索引查询优化案例
android nfc常用标签读取总结
东莞专业网站制作公司有哪些,东莞招聘网站哪个好?
如何在建站主机中优化服务器配置?
如何用搬瓦工VPS快速搭建个人网站?
详解Android——蓝牙技术 带你实现终端间数据传输
如何获取上海专业网站定制建站电话?
大连网站制作公司哪家好一点,大连买房网站哪个好?
Laravel模型关联查询教程_Laravel Eloquent一对多关联写法
php打包exe后无法访问网络共享_共享权限设置方法【教程】
如何在阿里云香港服务器快速搭建网站?
Python文件异常处理策略_健壮性说明【指导】
家族网站制作贴纸教程视频,用豆子做粘帖画怎么制作?
如何打造高效商业网站?建站目的决定转化率
大连网站制作费用,大连新青年网站,五年四班里的视频怎样下载啊?
Win11摄像头无法使用怎么办_Win11相机隐私权限开启教程【详解】
ChatGPT回答中断怎么办 引导AI继续输出完整内容的方法
创业网站制作流程,创业网站可靠吗?
DeepSeek是免费使用的吗 DeepSeek收费模式与Pro版本功能详解
如何自己制作一个网站链接,如何制作一个企业网站,建设网站的基本步骤有哪些?
JS中使用new Date(str)创建时间对象不兼容firefox和ie的解决方法(两种)
LinuxShell函数封装方法_脚本复用设计思路【教程】
Gemini手机端怎么发图片_Gemini手机端发图方法【步骤】
1688铺货到淘宝怎么操作 1688一键铺货到自己店铺详细步骤
网页设计与网站制作内容,怎样注册网站?
如何在阿里云购买域名并搭建网站?
香港服务器网站测试全流程:性能评估、SEO加载与移动适配优化
google浏览器怎么清理缓存_谷歌浏览器清除缓存加速详细步骤
大同网页,大同瑞慈医院官网?
javascript中的数组方法有哪些_如何利用数组方法简化数据处理

