异步单例模式的核心是所有并发调用共享并等待同一个初始化 Promise,确保初始化逻辑仅执行一次;通过静态 Promise 缓存实现,避免每次新建 Promise 导致重复请求。

异步单例模式的关键不是“阻止多次调用”,而是确保**所有并发调用都等待并共享同一个初始化过程的结果**——哪怕初始化是 Promise,也只执行一次,后续请求复用已 resolve 的 Promise 实例。
用静态 Promise 缓存 + 一次性初始化
最稳妥的做法是把初始化 Promise 存在静态属性里,首次调用时创建并缓存,后续直接返回该 Promise(而非每次都 new 一个):
- 初始化逻辑只运行一次,无论多少个 await 同时触发
- 所有 await 都等同一个 Promise,不会重复 fetch / connect / validate
- Promise 状态(fulfilled/rejected)天然可复用,无需手动锁或标记
示例:
class AsyncConfig {static #initPromise = null;
static async getInstance() {
if (!this.#initPromise) {
this.#initPromise = this.#loadConfig();
}
return this.#initPromise;
}
static async #loadConfig() {
const res = await fetch('/api/config');
return res.json();
}
}
避免常见错误:不要每次 new Promise
下面写法是错的——它每次调用都新建 Promise,导致并发请求重复执行:
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
立即学习“Java免费学习笔记(深入)”;
// ❌ 错误:每次调用都发起新请求static async getInstance() {
return fetch('/api/config').then(r => r.json());
}
- 两个 await AsyncConfig.getInstance() 可能同时触发两次 fetch
- 违背“仅初始化一次”的核心目标
- 尤其在组件 mount、路由守卫、插件加载等场景易暴露问题
支持重试与销毁的进阶处理
真实项目中常需应对失败重试或资源释放,可在缓存层加控制:
- 初始化失败时清空缓存,下次调用可重试(按需)
- 提供 clearCache() 或 destroy() 方法,主动释放 Promise 引用和内部资源(如 EventSource、WebSocket)
- 若需强制刷新,可传参标记 bypassCache,但默认仍走共享 Promise
例如:
static async getInstance({ force = false } = {}) {if (force) this.#initPromise = null;
if (!this.#initPromise) {
this.#initPromise = this.#loadConfig()
.catch(err => { this.#initPromise = null; throw err; });
}
return this.#initPromise;
}
模块级异步单例(更轻量、推荐用于配置/客户端)
借助 ES 模块的执行时序,直接导出一个初始化后的 Promise,天然单例且无竞态:
// config.jsexport const configPromise = fetch('/api/config').then(r => r.json());
// 使用处
import { configPromise } from './config.js';
const cfg = await configPromise;
- 模块加载即初始化,首次 import 触发 fetch,后续 import 共享同一 Promise
- 无 class、无方法调用开销,适合纯数据类单例(如配置、SDK 客户端)
- 注意:无法延迟初始化(非懒加载),也不支持运行时参数

















