JavaScript 实现发布订阅
事由
在开发微信小程序的过程中,因为要维护全局用户数据,所以各个页面都要从 app.js
同步用户数据;另外在一些其他的全局数据中也需要使用此功能。
在微信开发文档中只有通过 app.globalData
实现数据获取全局数据的方法。因为全局数据是从后端获取的,所以页面加载过程中不知晓全局数据什么时候准备好,所以在同步数据过程中只能延时获取,但该方法时间长了影响用户体验,时间短了并不能获取到数据;还可以循环调用直到数据完成同步,但这种方法需要检测数据内容,对数据有强依赖。
在今天查找解决方案的时候,才发现 JavaScript 中发布订阅的使用方法。
代码实现
class Event {
/** on 方法把订阅者所想要订阅的事件及相应的回调函数记录在 Event 对象的 cbs 属性中并调用 */
on(event, fn) {
if (typeof fn !== 'function') {
// eslint-disable-next-line no-console
console.error('fn must be a function');
return;
}
this.cbs = this.cbs || {};
this.data = this.data || {};
(this.cbs[event] = this.cbs[event] || []).push(fn);
if (this.data[event]) {
const callbacks = this.cbs[event];
callbacks?.forEach((callback) => {
callback(this.data[event]);
});
}
}
/** emit 方法接受一个事件名称参数,在 Event 对象的 cbs 属性中取出对应的数组,并逐个执行里面的回调函数 */
emit(event, data) {
this.cbs = this.cbs || {};
this.data = this.data || {};
this.data[event] = data;
const callbacks = this.cbs[event];
callbacks?.forEach((callback) => {
callback(data);
});
}
/** off 方法接受事件名称和当初注册的回调函数作参数,在 Event 对象的 cbs 属性中删除对应的回调函数。 */
off(event, fn) {
this.cbs = this.cbs || {};
this.data = this.data || {};
// all
if (!arguments.length) {
this.cbs = {};
return;
}
const callbacks = this.cbs[event];
if (!callbacks) return;
const data = this.data[event];
if (!data) return;
// remove all handlers
if (arguments.length === 1) {
delete this.cbs[event];
delete this.data[event];
return;
}
// remove specific handler
let cb;
for (let i = 0, len = callbacks.length; i < len; i += 1) {
cb = callbacks[i];
if (cb === fn || cb.fn === fn) {
callbacks.splice(i, 1);
break;
}
}
}
}
export default Event;