async函数不能直接构成单例,但可通过缓存Promise实现异步初始化仅执行一次:首次调用创建并缓存Promise,后续调用返回同一Promise,支持并发等待、错误重试与手动重置。

async 函数本身不能直接构成单例,但可以和单例模式结合,确保某个异步初始化操作(比如加载配置、连接数据库、获取 token)全局只执行一次,后续调用直接复用已解析的 Promise 或结果。
核心思路:缓存 Promise 而非值
单例的关键不是“只创建一个对象”,而是“只执行一次异步初始化”。由于 async 函数返回 Promise,最稳妥的方式是缓存这个 Promise 实例——它天然支持多次 await,且不会重复触发执行。
- 首次调用时执行异步逻辑,生成并缓存 Promise
- 后续调用直接返回已缓存的 Promise,所有 await 都等同一个结果
- 避免用 await 后再缓存值(如
result = await init()),否则无法处理初始化失败重试或并发等待场景
基础实现:闭包 + Promise 缓存
用一个私有变量保存初始化 Promise,函数每次返回它:
let initPromise = null;
async function getSingletonResource() {
if (!initPromise) {
initPromise = (async () => {
// 模拟耗时异步操作
await new Promise(r => setTimeout(r, 100));
return { data: 'cached result', timestamp: Date.now() };
})();
}
return initPromise;
}
这样连续调用 getSingletonResource() 多次,所有 await 都共享同一份结果和错误状态。
支持错误重试与重置
如果初始化可能失败,且你希望失败后能重试,可扩展为:当 Promise 被 reject 后清空缓存,下次调用重新发起请求:
- 捕获 reject,将
initPromise设为 null - 提供
reset()方法主动清空缓存 - 必要时加锁(如用
Promise.allSettled或简单标志位)防止并发重复初始化(多数场景下 Promise 缓存本身已足够)
封装成类或模块更易维护
把状态和逻辑收拢,比裸函数更清晰:
class AsyncSingleton {
#initPromise = null;
async #init() {
await new Promise(r => setTimeout(r, 200));
return { id: Math.random(), ready: true };
}
async getInstance() {
if (!this.#initPromise) {
this.#initPromise = this.#init().catch(err => {
this.#initPromise = null; // 失败则释放,允许重试
throw err;
});
}
return this.#initPromise;
}
reset() {
this.#initPromise = null;
}
}
// 使用
const instance = new AsyncSingleton();
const a = await instance.getInstance(); // 初始化
const b = await instance.getInstance(); // 直接返回缓存结果,a === b


















