用script标签的onload/onerror封装promise加载器,支持await、错误捕获、防重复加载(map缓存)、超时控制(promise.race)及资源清理(script.remove)。

<p>用 <code><script></code> 标签的 <code>onload</code> 和 <code>onerror</code> 封装 Promise 加载器,核心是让脚本加载变成可 await 的异步操作,同时兼顾错误捕获、重复加载控制和资源清理。</p> <h3>基础结构:创建 script 元素并返回 Promise</h3> <p>每次加载都新建 script 标签,避免复用导致状态混乱。Promise 在 onload 时 resolve,在 onerror 时 reject:</p> <p><font color="#666">关键点:必须设置 <code>src</code> 之后再绑定事件,否则 IE 可能漏触发;且需在插入 DOM 前绑定,防止竞态。</font></p>
- 动态创建
script元素,设置async=true(默认行为,但显式声明更清晰) - 先绑定
onload和onerror,再赋值src,最后插入document.head - onload 回调中调用
resolve(script),便于后续链式使用(如检查全局变量) - onerror 中调用
reject(new Error(...)),附带脚本 URL 和错误信息
防重复加载:用 Map 缓存正在加载或已加载的 Promise
相同 URL 多次调用应复用同一个 Promise,避免并发请求或重复插入 script:
- 用
Map<string, Promise<HTMLScriptElement>>存储 URL → Promise 映射 - 首次请求时生成新 Promise 并缓存;后续请求直接返回缓存 Promise
- 注意:缓存的是 Promise 实例,不是 script 元素本身——Promise 状态变化后仍可 await
- 无需手动清除缓存,除非有特殊内存要求;也可加 TTL 或按需清理
增强健壮性:超时控制 + 错误补充信息
网络卡顿或服务无响应时,Promise 不应无限等待。加上 timeout 可主动中断:
- 用
Promise.race([loaderPromise, timeoutPromise])包裹原始加载逻辑 - timeoutPromise 在指定毫秒后 reject,提示 “script load timeout”
- onerror 回调里可读取
event.target?.error或检查script.readyState补充上下文 - 加载失败后,建议移除该 script 元素(避免残留 DOM 节点),但注意不要影响其他逻辑
完整封装示例(可直接使用)
以下是一个轻量、无依赖、覆盖主流场景的实现:
<code>const scriptCache = new Map();
function loadScript(src, options = {}) {
const { timeout = 10000 } = options;
if (scriptCache.has(src)) {
return scriptCache.get(src);
}
const promise = new Promise((resolve, reject) => {
const script = document.createElement('script');
script.async = true;
script.onerror = () => {
script.remove();
reject(new Error(`Failed to load script: ${src}`));
};
script.onload = () => resolve(script);
// 设置超时
const timer = setTimeout(() => {
script.remove();
reject(new Error(`Script load timeout: ${src}`));
}, timeout);
// 必须在设置 src 前绑定事件
script.src = src;
document.head.appendChild(script);
// 清理定时器(成功或失败后)
const cleanup = () => clearTimeout(timer);
script.onload = () => {
cleanup();
resolve(script);
};
script.onerror = () => {
cleanup();
script.remove();
reject(new Error(`Failed to load script: ${src}`));
};
});
scriptCache.set(src, promise);
return promise;
}</code>
使用方式:await loadScript('/path/to/lib.js'),支持 try/catch 或 .then().catch(),也兼容 Promise.all 并行加载多个脚本。











