async和defer不提升js执行效率,只控制下载与执行时机;async脚本下载完立即执行,可能因dom未解析完导致document.getelementbyid返回null;defer脚本按顺序在dom解析完成后执行,适合有依赖的场景;混用二者会破坏执行顺序。

直接用 async 或 defer 不能提升“异步代码”的执行效率,它们只控制脚本的下载与执行时机,不改变 JS 本身的运行速度。真正影响执行效率的是脚本内容、体积、依赖关系和执行上下文。
为什么 async 会让 document.getElementById 返回 null
浏览器遇到 <script async src="app.js"></script> 时,会立即在后台下载,但一旦下载完成就**立刻执行**,完全不管 DOM 解析进度。如果 app.js 里写了 document.getElementById('header'),而此时 <div id="header"> 还没被解析到,结果就是 <code>null。
- 常见错误现象:
Uncaught TypeError: Cannot read property 'addEventListener' of null - 这不是 bug,是 async 的设计使然——它压根不等 DOM
- 修复方式:要么改用
defer,要么在脚本里加DOMContentLoaded监听,或用document.readyState判断 - 别把 async 脚本放在
里还指望它操作内容
defer 是多数业务脚本的默认选择
defer 脚本下载时不阻塞 HTML 解析,执行则严格卡在 DOM 解析完成之后、DOMContentLoaded 触发之前,且按书写顺序执行。这对依赖 DOM 或相互依赖的脚本(比如先加载 jquery.js,再加载 plugin.js)非常关键。
文章转信息图。将文章/笔记转化为手机可读的 HTML 信息图,自动匹配视觉风格。触发场景:文章转图、笔记转图、信息图、转小红书图、做张图、可视化这篇文章、文生图。
- 多个
defer脚本一定按<script defer src="a.js"></script>→<script defer src="b.js"></script>的顺序执行 -
defer不接受值,写成defer="true"或defer=""都无效,只写defer即可 - IE10+ 全支持,现代项目基本无兼容顾虑
- 注意:
type="module"脚本默认行为等效于defer,还带静态分析和预加载能力,新项目建议优先用
混用 async 和 defer 会导致执行顺序失控
一个页面里同时出现 async 和 defer 脚本,等于把调度权交给两个互不通信的机制:async 脚本一下载完就插队执行,defer 脚本则老老实实排队等 DOM 结束。结果往往是依赖未定义、ReferenceError 频发。
- 典型错误:
<script src="lib.js" defer></script>+<script src="init.js" async></script>→init.js可能在lib.js下载前就执行了 - 解决方案:要么全用
defer(保序),要么全用async(仅限无依赖脚本),要么用 JS 动态插入来精确控制时机 - 动态插入时务必监听
onload和onerror,否则失败静默,极难调试 -
preload不是替代方案:<link rel="preload" href="app.js" as="script">只提前下载,不改变执行逻辑
最常被忽略的一点:async/defer 对内联脚本无效,对 <script>console.log(1)</script> 加这两个属性毫无作用。需要异步执行内联逻辑,只能靠 setTimeout、Promise.resolve().then() 或事件监听兜底。










