async脚本执行顺序不可控,谁先下载完谁先执行;仅适用于完全独立的脚本(如统计、广告、错误监控),不依赖DOM或其他模块;内联脚本始终同步执行,不受async/defer影响。

async脚本执行顺序完全不可控
多个 async 脚本之间**没有执行顺序保证**,哪怕 <script async src="a.js"></script> 写在 <script async src="b.js"></script> 前面,只要 b.js 下载更快,它就会先执行。浏览器只管“谁下完谁跑”,不排队、不等待、不协调。
常见错误现象:ReferenceError: utils is not defined 或 TypeError: initApp is not a function,本质是依赖的函数/对象还没加载就调用了。
- async 只对带
src的外部脚本生效,内联脚本加async会被忽略 - 执行时会中断 HTML 解析(哪怕 DOM 还没建完),此时访问
document.getElementById可能返回null - 所有 async 脚本都是独立宏任务,彼此之间无事件循环调度依赖
什么情况下还能用 async?看它是否真“独立”
async 不是“不能用”,而是必须满足一个硬条件:该脚本既不读取也不修改 DOM,也不依赖其他 JS 模块导出的变量或函数。
典型适用场景:
立即学习“前端免费学习笔记(深入)”;
- 第三方统计代码(
analytics.js)——只发请求,不碰页面结构 - 广告 SDK(
ad-banner.js)——自己创建 iframe 或 script 标签注入 - 错误监控(
error-tracker.js)——全局监听window.onerror,不依赖任何前置逻辑
一旦出现 import、require、utils.init()、document.querySelector 等调用,就说明它不独立,不该用 async。
defer 是 async 的替代解,但有前提
defer 能解决顺序问题,但它要求脚本**全部是外部文件**,且**必须按依赖顺序声明**。比如 utils.js 必须写在 main.js 前面,否则 main.js 执行时 utils 还没定义。
关键限制:
- 多个
defer脚本严格按 HTML 中出现顺序执行,哪怕main.js下载慢于utils.js,也等它一起等到 DOM 解析完再顺序跑 -
defer不适用于需要尽早运行的脚本(比如首屏性能埋点),它一定会等到DOMContentLoaded前才执行 - 如果某个脚本要操作
<body>里刚解析出来的元素,但该元素在<script defer>后面,那没问题;但如果元素在<script defer>前面而脚本又没加defer,就可能拿不到
真正复杂的其实是混合使用场景
现实项目里经常混用:async 加统计脚本、defer 加业务逻辑、底部还留一个内联 <script>init();</script>。这时候最容易被忽略的是:内联脚本永远同步执行,它会卡在它出现的位置,不管前后有没有 async 或 defer。
例如:
<script async src="analytics.js"></script>
<script defer src="utils.js"></script>
<script>console.log('inline');</script>
<script defer src="main.js"></script>
这个 inline 脚本会在 analytics.js 下载期间就执行(甚至 DOM 都没开始解析),它看不到 utils.js 或 main.js 里的任何东西,也大概率拿不到完整 DOM。



















