
本文介绍一种基于 async/await 的现代方案,用于确保多个外部 JavaScript 文件严格按序加载、执行并等待完成后再加载下一个,避免回调嵌套与事件监听的复杂性。
本文介绍一种基于 `async/await` 的现代方案,用于确保多个外部 javascript 文件严格按序加载、执行并等待完成后再加载下一个,避免回调嵌套与事件监听的复杂性。
在前端开发中,有时需要按特定顺序加载并执行多个外部脚本(例如依赖链明确的初始化模块、分阶段的 SDK 加载或需严格时序控制的业务逻辑)。虽然 <script defer> 能保证执行顺序与 DOM 中声明顺序一致,但它<strong>不保证脚本内容执行完毕后才开始下一个脚本的加载或执行——尤其当脚本内含异步操作(如 setTimeout、fetch 或自定义 Promise)时,仅靠 defer 无法满足“前一个彻底完成 → 后一个才启动”的强序要求。</script>
更简洁、健壮且符合现代 JavaScript 实践的解决方案是:使用 async/await 配合动态创建 <script> 元素,并通过 onload 事件 resolve Promise,实现真正的串行控制流</script>。
以下是一个完整、可复用的实现:
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
<title>按序加载脚本</title><script>
// 封装单个脚本加载为 Promise
async function loadScript(src) {
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = src;
script.type = 'module'; // 可选:若脚本使用 ES 模块语法,建议添加
script.onload = () => {
console.log(`✅ 已加载并执行完毕: ${src}`);
resolve();
};
script.onerror = (err) => {
console.error(`❌ 脚本加载失败: ${src}`, err);
reject(new Error(`Failed to load script: ${src}`));
};
document.head.appendChild(script);
});
}
// 串行加载脚本列表
async function loadScriptsSequentially(scripts) {
for (const src of scripts) {
try {
await loadScript(src);
} catch (error) {
console.error(`中断于 ${src}:`, error.message);
throw error; // 可根据需求改为 continue 或降级处理
}
}
console.log('? 所有脚本已按序加载并执行完成');
}
// 使用示例:支持任意长度数组(如 20 个脚本)
const scriptUrls = [
'script1.js',
'script2.js',
'script3.js'
// ... 更多脚本
];
// 启动加载流程
loadScriptsSequentially(scriptUrls);
</script><h1>Hello world</h1>
✅ 优势说明:
- 语义清晰:await loadScript(...) 直观表达“等待此脚本完全就绪”,逻辑线性无嵌套;
- 错误可控:每个 loadScript() 都有独立 catch 分支,便于定位失败节点并做重试或告警;
- 扩展性强:轻松适配 3 个或 20 个脚本,无需修改结构;
- 兼容性好:只要浏览器支持 async/await(Chrome 55+、Firefox 52+、Safari 10.1+),即可运行;
- 不依赖全局事件:避免手动 dispatchEvent 和 addEventListener,减少命名冲突与内存泄漏风险。
⚠️ 注意事项:
- 若脚本本身包含顶级 await(ES 模块顶层 await),需确保 <script type="module">,并在 loadScript 中显式设置 script.type = 'module'; </script>
- 动态插入的脚本默认以 text/javascript 执行,不参与 defer/async 属性解析,因此本方案天然规避了其行为不确定性;
- 生产环境建议增加超时机制(如 AbortController + setTimeout 包裹 Promise),防止某脚本长期挂起阻塞后续流程;
- 对于纯静态资源(如无副作用的工具库),若仅需加载顺序而不要求执行完成,则 defer 仍是最轻量方案;但本文聚焦「执行完成即触发下一环节」这一更强约束场景。
综上,利用 async/await 驱动动态脚本加载,是以最小认知成本达成最高可靠性的标准实践,推荐作为顺序执行脚本的首选模式。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










