defer脚本在dom构建完成且按html顺序执行,async脚本下载完立即执行、无序且不等dom;二者均不阻塞html解析,但触发时机与顺序保障机制截然不同。

识别 defer 和 async 对脚本执行时机的影响,关键不是看“谁先写”,而是看“谁什么时候真正开始跑”。它们都不阻塞 HTML 解析,但执行的触发条件完全不同——一个等 DOM 就绪,一个抢在下载完就跑。
defer:DOM 构建完才统一执行,顺序稳如磐石
带 defer 的脚本会在整个 HTML 解析完成、DOM 树完全生成后,按它们在页面中出现的先后顺序依次执行。这个时机发生在 DOMContentLoaded 事件触发前,所以你能安全使用 document.getElementById、querySelector 或绑定事件监听器。
- 多个 defer 脚本即使下载速度不同,也严格按 HTML 中的书写顺序执行(A 在 B 前 → A 一定先于 B 执行)
- 哪怕 script2.js 比 script1.js 先下载完,它也必须等 script1.js 执行完再启动
- 适合初始化逻辑、表单校验、依赖 DOM 的组件加载
async:谁先下完谁先跑,不等别人也不等 DOM
async 脚本一旦下载完成,浏览器会立即中断当前 HTML 解析(如果还在进行中),执行该脚本。它的执行时机不可预测——可能在
- 多个 async 脚本之间无执行顺序保障(scriptB 写在后面,但先下载完 → 它先执行)
- 执行时 DOM 可能尚未构建完成,
document.getElementById很可能返回null - 适合统计埋点、广告 SDK、独立功能模块等不依赖其他脚本或 DOM 结构的代码
一眼识别执行时机差异的实操方法
打开浏览器开发者工具 → 切到 Network 面板 → 刷新页面 → 查看每个 JS 文件的 “Waterfall” 时间轴:
- defer 脚本:下载条出现在早期(和 HTML 并行),但执行时间点明显卡在 HTML 解析结束之后、DOMContentLoaded 触发之前
- async 脚本:下载条同样早,但执行标记(通常是黄色小竖线或“Execute”提示)紧贴在下载完成之后,位置随机,可能穿插在 HTML 解析过程中
- 再配合 Console 面板输出
console.log('script1 loaded')等调试语句,结合 Performance 面板查看DOMContentLoaded和load事件时间戳,就能验证实际执行顺序是否符合预期
常见误判点提醒
别被“标签位置”迷惑:async 不因写在前面就优先执行;defer 也不因网络慢就跳过顺序。真正决定执行节奏的是浏览器的内部调度规则,不是你写的顺序,而是你选的属性。
同个 script 标签上同时写 async defer,浏览器会忽略 defer,只按 async 行为处理。











