Deferred Pattern 用于确保异步初始化只执行一次,避免重复请求和竞态问题;它通过抽离 resolve/reject 控制权实现多模块共享同一 Promise 结果,相比闭包缓存更灵活支持跨回调、事件等场景。

Promise 本身没有“状态锁”概念,但你提到的 Deferred Pattern(延迟模式)常被用来模拟类似行为——即在异步操作完成前,多次调用同一个 Promise 的 then/catch 不会重复执行逻辑,而是共享最终结果。这在多模块异步初始化中非常实用:多个模块可能依赖同一份配置、权限、用户信息等,只需初始化一次,后续请求全部复用结果。
为什么需要 Deferred Pattern?
原生 Promise 一旦 resolve/reject 就不可逆,但它的构造函数是“立即执行”的。如果多个模块各自 new Promise(...init()),就会触发多次初始化,造成资源浪费甚至竞态问题。Deferred Pattern 的核心是:把 Promise 的 resolve/reject 控制权抽离出来,由一个中心逻辑统一管理,确保初始化只发生一次。
手动实现一个轻量 Deferred 工具
不需要第三方库,几行代码就能封装:
class Deferred {
constructor() {
this.promise = new Promise((resolve, reject) => {
this.resolve = resolve;
this.reject = reject;
});
}
}
使用示例(如初始化用户信息):
立即学习“Java免费学习笔记(深入)”;
let userDeferred;
function initUser() {
if (!userDeferred) {
userDeferred = new Deferred();
fetch('/api/user')
.then(res => res.json())
.then(data => userDeferred.resolve(data))
.catch(err => userDeferred.reject(err));
}
return userDeferred.promise;
}
// 模块 A
initUser().then(user => console.log('A got:', user));
// 模块 B(几乎同时调用)
initUser().then(user => console.log('B got:', user));
// ✅ 两者共享同一个 Promise,只发一次请求
在多模块中的协作模式
- 统一初始化入口:定义一个 init() 函数(如 initCoreServices()),内部按需创建多个 Deferred 实例(configDeferred、authDeferred、i18nDeferred 等)
- 模块按需订阅:各模块不主动触发初始化,而是调用 getXXX() 方法获取对应 Promise,内部自动触发或复用
- 错误可重试:可在 Deferred 外层包装 retry 逻辑,或暴露 reset() 方法清空状态重新开始
- 避免内存泄漏:Deferred 实例长期存在没问题,但不要在每次调用都新建;建议挂载到全局服务对象或模块作用域静态变量上
现代替代方案:async/await + 单例缓存
如果你用的是较新环境,也可以不用显式 Deferred,改用闭包缓存 Promise 实例:
let _configPromise;
function getConfig() {
if (!_configPromise) {
_configPromise = fetch('/config.json')
.then(r => r.json())
.catch(err => {
_configPromise = null; // 可选:失败后允许重试
throw err;
});
}
return _configPromise;
}
这种方式更简洁,语义清晰,且天然支持 await,适合大多数场景。Deferred Pattern 的优势在于对 resolve/reject 的细粒度控制(比如跨回调、事件、定时器触发),而不仅仅是 HTTP 请求。


















