Promise本身不是发布订阅模式,但可通过状态变化模拟:用数组存回调,状态确定后统一执行,实现订阅→发布流程;支持单次和多次发布的两种封装方式。

Promise 本身不是发布订阅模式,但可以基于 Promise 的状态变化(resolve/reject)模拟发布订阅行为。核心思路是:用一个数组存储多个回调函数,在 Promise 状态确定后统一执行它们——这本质上就是“订阅(注册回调)→ 发布(触发状态变更)”的过程。
用 Promise 封装一个简易发布订阅类
通过维护 pending 状态的 Promise 和订阅队列,实现类似 EventEmitter 的 on/emit 能力:
class PubSub {
constructor() {
this.handlers = []; // 存储所有待执行的回调
this.promise = null;
this.resolveFn = null;
this.rejectFn = null;
}
// 订阅:返回一个 Promise,让调用方 await 或 .then
subscribe() {
if (!this.promise) {
this.promise = new Promise((resolve, reject) => {
this.resolveFn = resolve;
this.rejectFn = reject;
});
}
return this.promise;
}
// 发布:触发所有已订阅的 Promise 完成(可带数据)
publish(data) {
if (this.resolveFn) {
this.resolveFn(data);
this.reset();
}
}
// 发布错误
publishError(err) {
if (this.rejectFn) {
this.rejectFn(err);
this.reset();
}
}
reset() {
this.promise = null;
this.resolveFn = null;
this.rejectFn = null;
}
}
// 使用示例
const bus = new PubSub();
bus.subscribe().then(data => console.log('收到:', data)); // 订阅1
bus.subscribe().then(data => console.log('也收到:', data)); // 订阅2
setTimeout(() => bus.publish('hello world'), 100); // 发布 → 两个订阅同时响应
支持多次发布(多播)的增强版
上面版本只支持单次发布(Promise 一旦 resolve 就不可再用)。若需多次 emit,可改用「每次 subscribe 返回新 Promise」+「内部广播队列」的方式:
class MultiPubSub {
constructor() {
this.subscribers = []; // 存储 resolve 函数
}
subscribe() {
return new Promise(resolve => {
this.subscribers.push(resolve);
});
}
publish(data) {
// 克隆数组,避免遍历时修改原数组出错
const resolves = [...this.subscribers];
resolves.forEach(resolve => resolve(data));
}
// 可选:取消某个订阅(需返回取消方法)
subscribeWithCancel() {
let isCanceled = false;
const p = new Promise(resolve => {
const handler = data => {
if (!isCanceled) resolve(data);
};
this.subscribers.push(handler);
});
return {
promise: p,
cancel: () => { isCanceled = true; }
};
}
}
和原生 EventEmitter 的关键区别
- 无事件名区分:上述实现是“全量广播”,不像 node EventEmitter 支持 on('click') / emit('click');如需事件名,得在 publish 时加 type 字段,并在 subscribe 里过滤。
- 无同步执行保障:Promise 回调总是异步(microtask),即使 publish 立即调用,.then 也会等当前同步代码结束后才执行。
- 不自动清理:Promise 不会自动释放订阅,长期运行需手动管理(如提供 unsubscribe 方法或用 WeakMap 关联)。
实际场景建议
- 简单组件通信(如父子、兄弟组件间通知)可用 Promise + async/await 写法,语义清晰;
- 高频、多类型、需取消的场景,优先用标准 EventEmitter 或 RxJS Subject;
- 服务初始化完成通知(如 auth ready、config loaded),适合用单次 resolve 的 Promise 模式,天然符合“只发一次”语义。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











