async属性在ie10+及现代浏览器中均支持,但脚本内容因执行过早易导致dom未就绪、全局变量缺失等问题,仅适用于无dom和依赖的独立脚本。

async 属性本身兼容性没问题,但脚本内容可能不兼容
async 属性在 IE10+、Edge、Chrome、Firefox、Safari 中全部支持,无需 polyfill。真正出问题的不是属性本身,而是你写在 async 脚本里的代码——它执行得太早、太独立,容易踩到环境缺失的坑。
常见错误现象包括:
-
document.getElementById('app')返回null -
$或monaco报ReferenceError - 控制台显示
document.readyState是"loading"或"interactive"
这说明脚本执行时 DOM 还没解析完,或依赖的全局变量根本不存在。async 不负责等任何东西,只管“下完就跑”。
适用边界很窄:必须满足三个条件才适合加 async:
- 脚本是纯外部文件(带
src),不含内联逻辑 - 不读写任何 DOM 节点(包括
document.body、document.head) - 不依赖任何其他 JS 变量、函数或库(比如 jQuery、Lodash、Monaco)
典型可用场景只有:统计埋点(如 Plausible、Umami)、广告 SDK、性能监控上报。
为什么 async 脚本里调用 document.write 会清空整个页面
document.write 在页面加载完成后调用会直接重写文档,而 async 脚本可能在 DOMContentLoaded 之前、甚至 HTML 解析中途就执行——此时调用 document.write 等同于在 parser 阶段插入内容,浏览器只能清空重建。
必须做两件事:
- 彻底移除
document.write,改用document.createElement+appendChild或innerHTML - 如果第三方 SDK 内部用了
document.write(老版本百度统计、部分广告脚本),不能加async,得改用动态插入 +onload回调控制时机
注意:async 脚本中哪怕只有一行 document.write(''),也会导致整页白屏,且无明确报错,只在 Network 面板看到文档被 replace。
CDN 资源跨域时 async 脚本报错看不到堆栈怎么办
Chrome 对跨域脚本的错误信息做了限制:如果 <script src="https://cdn.example.com/analytics.js"></script> 没带 crossorigin="anonymous",一旦执行出错,控制台只显示 Script error.,无法定位具体哪一行。
解决方法很简单,但极易被忽略:
- 所有跨域
async脚本都必须显式添加crossorigin="anonymous" - 如果 CDN 不支持 CORS(比如某些老旧静态资源站),要么换源,要么放弃
async改用动态加载 +try/catch
示例正确写法:
<script async src="https://plausible.io/js/script.js" crossorigin="anonymous"></script>
不加 crossorigin 的后果不是加载失败,而是调试失能——你永远不知道脚本到底执行到哪一步挂了。
如何验证 async 是否真按预期工作
别只看 HTML 里写了 async 就以为生效了。实际是否起效,得看网络和执行时序:
- 打开 Chrome DevTools → Network 面板,筛选
JS,找目标脚本,看它的Initiator是parser还是other:如果是parser,说明下载确实没阻塞 HTML 解析 - 切到 Performance 面板,录制一次刷新,搜索该脚本名,看执行时间点是否落在 HTML Parse 区间内(async 允许打断解析执行),而不是等到
DOMContentLoaded之后(那是 defer 的行为) - 在脚本开头加
console.log(document.readyState, Date.now()),观察输出值:若常为"loading"且时间戳远早于其他脚本,基本可以确认是 async 行为
最容易被忽略的一点:多个 async 脚本之间完全不保序。哪怕 A.js 在 HTML 中写在 B.js 前面,只要 B 下载更快,B 就先执行——这对初始化逻辑是致命的。需要顺序,就别用 async。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











