动态加载script后不能仅依赖onload事件判断执行完成,因其只表示下载和解析完毕,不保证异步逻辑或全局变量已就绪;应通过轮询检测预期api是否存在,或优先使用import()配合显式初始化函数。

script动态加载后如何知道它执行完了
动态创建 <script></script> 标签后,不能靠 onload 或 onerror 100% 确保“脚本逻辑已执行完毕”,因为这两个事件只表示文件下载并解析完成,不保证内部代码(比如异步初始化、DOMContentLoaded 监听、模块注册)已完成。真正关心的是“该脚本暴露的 API 或全局变量是否可用”。
为什么不能只依赖 onload 事件
onload 触发时,脚本只是被浏览器解析执行完顶层语句——但若脚本里有 setTimeout、fetch、Promise、或依赖 DOM 就绪的逻辑,此时 window.xxx 可能还没挂载上,直接调用会报 ReferenceError。
-
onload是“下载+执行完毕”的信号,不是“功能就绪”的信号 - 第三方 SDK(如微信 JS-SDK、支付宝 alipay-jsapi)常在
document.readyState === 'complete'后才初始化,或需手动alipay.ready(...) - ESM 动态导入(
import())天然支持 Promise,但传统<script></script>不行
安全判断全局变量是否就绪的轮询方案
最通用、兼容性最好的做法:在 onload 后启动一个轻量轮询,检测脚本预期暴露的变量或函数是否存在。避免死等,加超时保护。
function loadScript(src, callback, timeout = 5000) {
const script = document.createElement('script');
script.src = src;
script.async = true;
const timer = setTimeout(() => {
callback(new Error('Script load timeout'));
}, timeout);
script.onload = () => {
clearTimeout(timer);
// 假设脚本应挂载 window.myLib
const checkReady = () => {
if (typeof window.myLib === 'function') {
callback(null, window.myLib);
} else {
setTimeout(checkReady, 10);
}
};
checkReady();
};
script.onerror = () => {
clearTimeout(timer);
callback(new Error(`Failed to load ${src}`));
};
document.head.appendChild(script);
}
- 轮询间隔 10ms 足够快,又不会压垮主线程
- 超时时间建议设为 5s,太短易误判,太长阻塞业务
- 不要轮询
document.readyState来代替变量检测——有些脚本根本不依赖 DOM
现代替代方案:优先用 import() + 导出规范
如果脚本是你自己控制的,强烈建议改造成 ES 模块,并导出明确的初始化函数。这样可彻底避开轮询:
// my-lib.js
export function init() {
return new Promise(resolve => {
// 真正的初始化逻辑(可能含异步)
setTimeout(() => {
window.myLib = { version: '1.0' };
resolve(window.myLib);
}, 100);
});
}
调用侧直接:
import('./my-lib.js').then(module => {
return module.init(); // 等待它真正的 ready
}).then(lib => {
console.log('Ready:', lib);
});
-
import()返回 Promise,天然支持链式等待 - 要求服务器支持 MIME type
application/javascript,且脚本路径必须是相对或绝对 URL(不能是跨域无 CORS 的 CDN) - IE 完全不支持,老项目需搭配
SystemJS或降级回轮询
轮询不是“土办法”,而是面对不可控第三方脚本时最实际的解法;而 import() 是可控环境下的首选。别把 onload 当成终点,它只是起点。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











