promise可优雅封装动态脚本加载,支持链式调用、错误捕获与组合;核心是创建script元素并监听load/error事件来resolve/reject;提供基础loadscript函数、串行(reduce)、并行(promise.all)及增强版(缓存+超时)实现。

Promise 可以优雅地封装动态脚本加载过程,让异步加载变得可链式调用、可捕获错误、可与其他 Promise 组合(如 Promise.all)。核心思路是:创建 <script></script> 元素,监听其 load 和 error 事件,并在对应时机 resolve 或 reject。
基础实现:一个可复用的 loadScript 函数
这是最常用、最简洁的写法:
function loadScript(src) {
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = src;
script.async = true;
script.onload = () => resolve(script);
script.onerror = () => reject(new Error(`Failed to load script: ${src}`));
document.head.appendChild(script);
});
}
使用示例:
loadScript('https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js')
.then(script => console.log('Lodash loaded', script))
.catch(err => console.error(err));
支持多个脚本按序加载(串行)
如果依赖顺序(例如先加载 jQuery,再加载插件),可用 reduce 链式加载:
- 每个
loadScript返回 Promise,Promise.resolve()作为初始值 - 依次
then加载下一个,形成串行队列
const scripts = [
'https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js',
'https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js'
];
scripts.reduce((chain, src) => {
return chain.then(() => loadScript(src));
}, Promise.resolve())
.then(() => console.log('All scripts loaded in order'))
.catch(err => console.error('Load failed:', err));
并行加载多个脚本并统一处理
适合无依赖关系的资源(如统计 SDK、埋点脚本),用 Promise.all 并发加载:
- 所有脚本同时发起请求,整体耗时取决于最慢的一个
- 任一失败则整个 Promise 被 reject
const urls = [
'https://example.com/analytics.js',
'https://example.com/tracker.js',
'https://example.com/feature-flag.js'
];
Promise.all(urls.map(loadScript))
.then(scripts => console.log('All loaded:', scripts))
.catch(err => console.error('One failed:', err));
增强版:避免重复加载 + 支持超时控制
实际项目中常需防止重复插入同一脚本,还可加入超时保护(防资源长期不响应):
- 用 Map 缓存已加载或加载中的 Promise,相同 URL 复用
- 用
setTimeout+reject实现超时逻辑,记得清理定时器
const loadingCache = new Map();
function loadScript(src, timeout = 10000) {
if (loadingCache.has(src)) {
return loadingCache.get(src);
}
const promise = new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = src;
script.async = true;
const timer = setTimeout(() => {
script.remove();
reject(new Error(`Script timeout: ${src}`));
}, timeout);
script.onload = () => {
clearTimeout(timer);
resolve(script);
};
script.onerror = () => {
clearTimeout(timer);
reject(new Error(`Script load error: ${src}`));
};
document.head.appendChild(script);
});
loadingCache.set(src, promise);
return promise;
}
不复杂但容易忽略:动态加载的脚本执行后,其全局变量(如 $、_)会立即可用;但若脚本内部依赖 DOM 就绪,则仍需确保在 DOMContentLoaded 后调用或自行判断。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











