async脚本执行时变量未定义是因加载时序问题:async不保证执行顺序,脚本可能在依赖的全局变量(如initData)声明前就执行;需确保变量提前声明、改用defer、轮询等待或检查构建配置。

async 脚本执行时变量未定义的典型现象
页面里用 <script async src="app.js"></script> 加载 JS,但 app.js 里一上来就引用了 window.config 或 initData 这类全局变量,控制台报 ReferenceError: initData is not defined —— 这不是代码写错了,是加载时序出了问题:async 不保证执行顺序,脚本可能在 DOM 解析完前就下载完成并立刻执行,而变量定义很可能在后续 HTML 或其他 script 中。
确认变量是否真被声明、且声明早于 async 脚本
async 脚本不阻塞 HTML 解析,但它依赖的变量必须已在它执行前挂到全局(比如 window)上。常见错误包括:
- 变量定义在另一个
<script>标签里,但该标签没加async或defer,且位置在 async 脚本之后 - 变量定义在
<body>底部的内联 script 中,但 async 脚本下载快、执行早,抢在它前面运行 - 使用了模块化打包(如 Webpack),但入口文件没显式导出到全局,
window.xxx实际为空
调试时直接在浏览器控制台输 typeof initData 或 window.initData,刷新页面多次观察:如果有时有、有时无,基本就是竞态;如果始终 undefined,说明变量压根没声明或声明失败(比如 JSON 解析出错、JS 报错中断执行)。
让 async 脚本安全访问变量的几种实操方式
- 把变量声明提前:把
<script>window.initData = {...}</script> 放在 <head> 最顶部,确保它最早执行
- 用
defer 替代 async:如果脚本依赖 DOM 或全局变量,且不需要尽早执行,改用 <script defer src="app.js"></script>,它会按顺序执行,且等 DOM 解析完
- 主动轮询等待变量就绪(仅限兜底):
function waitForGlobal(name, callback) {
if (window[name] !== undefined) return callback(window[name]);
setTimeout(() => waitForGlobal(name, callback), 10);
}
waitForGlobal('initData', data => {
// 正常逻辑
});
- 检查构建产物:如果是打包工具生成的 async 脚本,确认
output.globalObject: 'window'(Webpack)或 globals 配置正确,避免变量被闭包隔离
Chrome DevTools 中快速定位执行时机
<script>window.initData = {...}</script> 放在 <head> 最顶部,确保它最早执行 defer 替代 async:如果脚本依赖 DOM 或全局变量,且不需要尽早执行,改用 <script defer src="app.js"></script>,它会按顺序执行,且等 DOM 解析完 function waitForGlobal(name, callback) {
if (window[name] !== undefined) return callback(window[name]);
setTimeout(() => waitForGlobal(name, callback), 10);
}
waitForGlobal('initData', data => {
// 正常逻辑
}); output.globalObject: 'window'(Webpack)或 globals 配置正确,避免变量被闭包隔离打开 DevTools → Network → 找到你的 async 脚本 → 点开 Timing 标签页,看 Start Time 和 End Time 是否远早于 DOMContentLoaded;再切到 Sources → Event Listener Breakpoints → 勾选 Load 和 DOMContentLoaded,刷新后对比脚本执行栈和事件触发点。如果 async 脚本堆栈里没有 document.write 或同步 DOM 操作,那它大概率是在纯 JS 上下文里跑的,此时全局变量必须已存在。
立即学习“前端免费学习笔记(深入)”;
async 的本质是“尽快执行”,不是“安全执行”。变量未定义从来不是 async 的 bug,而是你没管住依赖链的时序。真正要盯紧的,是那个被依赖的变量——它在哪声明、谁负责初始化、有没有失败静默。



















