合并脚本是唯一能用async又不乱序的办法;将有依赖关系的脚本物理合并为单个文件再async加载,可彻底消除乱序风险,其他如defer、调整加载顺序等均无法可靠解决async固有的无序执行问题。

不能靠合并来防止乱序——async 本身就不保序,合并多个脚本成一个文件才是唯一可靠解法。其他“技巧”如加 defer、改写加载顺序、或动态插入带依赖检查的代码,本质都是绕开 async 的设计前提,反而增加复杂度和失败概率。
async 脚本乱序是设计使然,不是 bug
浏览器对 async 的实现逻辑很明确:每个带 async 的 <script></script> 独立下载、下载完立刻执行,不等 DOM、不等其他脚本。哪怕你在 HTML 里把 utils.js 写在 main.js 前面,只要后者先下载完,它就先执行。
- 典型报错:
ReferenceError: initApp is not defined,因为main.js执行时utils.js还没加载完 -
async不受 HTML 书写顺序约束,也不参与任何队列调度 - IE10 及以下直接忽略
async,退化为同步阻塞(若需兼容,必须另做降级)
合并脚本是唯一能用 async 又不乱序的办法
把有依赖关系的脚本物理合并为单个文件,再用 async 加载,就能避开顺序问题——因为只剩一个脚本,无所谓“谁先谁后”。
- 合并前:
<script async src="lib.js"></script>+<script async src="app.js"></script>→ 高概率乱序 - 合并后:
<script async src="bundle.js"></script>→ 无序风险归零 - 构建时用 Webpack/Rollup/Vite 打包,或简单 cat 拼接(注意变量/函数名冲突)
- 如果某脚本是第三方不可控的(如广告 SDK),它必须独立
async,其余业务代码就得避开依赖它
别用 defer 替代 async 来“修复”乱序
defer 确实保序,但它和 async 是互斥机制。混用会破坏预期行为:
-
<script async src="analytics.js"></script>和<script defer src="app.js"></script>同时存在 →analytics.js可能在app.js还没开始下载时就执行完毕,污染全局或覆盖变量 -
defer要求所有脚本都外部引入、且严格按 HTML 顺序声明;一旦夹杂内联脚本或动态插入,保序即失效 - 想兼顾“不阻塞”和“保序”,就该统一用
defer,而不是给部分脚本加async、部分加defer
真正需要 async 时,只留给无依赖脚本
如果你的页面必须同时用到埋点、广告、监控等第三方脚本,它们天然适合 async——因为设计上就不该被其他逻辑依赖。
- 确认这些脚本是否真的“完全独立”:不调用你代码里的函数、不读写你定义的全局变量、不操作
document.body等 DOM 节点 - 若发现某第三方 SDK 实际上会挂载
window.SDK供你后续调用,那它就不该async,而应改用defer或动态加载 +onload回调 - 动态加载可加错误隔离:
script.onerror = () => console.warn('SDK load failed'),避免因一个失败导致整页 JS 崩溃
最常被忽略的一点:async 不是性能优化的万能开关,而是对脚本行为的明确承诺——“我无需别人,也不给别人依赖”。一旦打破这个契约,就只能换方案,没法打补丁。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











