async脚本执行时document.getelementbyid返回null,是因为它下载完立即执行,不等待html解析完成,导致目标dom元素尚未创建;常见于head中操作body节点,引发“cannot read property 'addeventlistener' of null”等错误。

async 脚本为什么执行时 document.getElementById 会返回 null
因为 async 脚本下载完就立即执行,完全不等 HTML 解析完成。哪怕它写在 里,也可能在 还没开始解析时就跑起来了。
常见错误现象:Uncaught TypeError: Cannot read property 'addEventListener' of null,本质就是脚本执行太早,DOM 元素根本还没被创建出来。
- 执行时机不可控:无法预测它在哪个 DOM 节点前/后执行
- 不支持内联脚本:
<script async>console.log('hi')</script>中的async被忽略,脚本立刻同步执行 - 只对带
src的外部脚本生效;async="false"或async="true"没有意义,它只是布尔属性,写了就启用
async 和 defer 都不阻塞解析,但执行时机差得远
async 是“下载完立刻中断解析来执行”,defer 是“下载完先放着,等整个 HTML 解析完、DOMContentLoaded 前再统一按顺序执行”。
这意味着:
- 多个
async脚本谁先下完谁先跑,顺序完全随机;jquery.js和plugin.js都标了async,后者可能先执行,报错$ is not defined -
defer脚本能安全访问document.body、document.getElementById('app'),且顺序严格按 HTML 里写的顺序 -
async和defer不能共存,浏览器只认async,defer被丢弃
哪些脚本适合加 async?别乱用
只适合完全独立、不操作 DOM、不依赖其他 JS、也不被其他 JS 依赖的脚本——典型就是埋点、广告 SDK、性能监控上报这类。
- ✅ 合适:
<script async src="analytics.js"></script>,它只发请求,不查元素,不调其他函数 - ❌ 不合适:
<script async src="init.js"></script>,里面写了document.querySelector('#form').addEventListener(...) - ⚠️ 特别注意:
type="module"脚本天然具有defer行为(异步下载 + 解析完执行 + 顺序保证),不需要、也不能加async
async 脚本执行会中断 HTML 解析,不是“完全不卡”
很多人以为加了 async 就全程不阻塞,其实不然:下载阶段确实并行,但执行那一刻,浏览器会暂停 HTML 解析,运行脚本,再 resume 解析。这个中断可能导致渲染短暂卡顿,尤其脚本体积大或逻辑重时。
真正“无感”的加载方式,往往不是靠 async,而是:
- 把初始化逻辑挪到
DOMContentLoaded或load事件里 - 用
document.createElement('script')动态插入,并控制插入时机(比如用户交互后) - 对非首屏功能做 code-splitting +
import()动态导入
async 的核心价值是避免“初始下载阻塞”,而不是解决“执行时机混乱”——这点最容易被忽略。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











