记录小知识系列(1)

44 阅读1分钟

前言

从来也没想着写些啥的,但是偶尔翻开以前的记录,发现忘了好多事,以后有空就记录一下吧。

发布订阅模式

今天看见一篇很好的文章,那第一篇文章就从抄开始吧,程序员一定要学会CV。文章来源:

手撕设计模式: 发布订阅模式基础实践

希望作者大大勿怪。

效果演示

模拟接口请求的状态,并做相应处理。比如在监听到一个错误码,需要刷新页面的时候,发布订阅模式做为一个中间层。如果需要处理大量的状态码,这不失为一个好的方式。

代码

/**
 * 扩展需要根据自身业务需求来定义
 * 实现中使用 Set 存订阅事件及先发布后订阅的事件参数,想的是去重,
 * 重复订阅事件, 先发布后订阅多次相同参数的事件 都只存一次
 * 可以根据业务需求,来决定用Array存还是Set存
 */
type EventCallback = (...args: any[]) => void;

class EventManager {
    static #instance: EventManager;
    #listeners: Record<string, Set<EventCallback>> = {};
    #pending: Record<string, Set<any[]>> = {}
    private constructor() {}
    static getInstance() {
        return EventManager.#instance ??= new EventManager();
    }
    on(event: string, listener: EventCallback) {
        ;(this.#listeners[event]??= new Set()).add(listener);
        if(this.#pending[event]){
            this.#pending[event].forEach((args)=>listener(...args));
            delete this.#pending[event];
        }
    }
    emit(event: string, ...args: any[]) {
        if (this.#listeners[event]) {
            this.#listeners[event].forEach(cb=>cb(...args))
        } else {
            ;(this.#pending[event]??=new Set()).add(args)
        }
    }
    off(event: string, listener: EventCallback) {
        this.#listeners[event]?.delete(listener);
    }
    once(event: string, listener: EventCallback) {
        const wrapper = (...args: any[])=>{
            listener(...args);
            this.off(event, wrapper);
        }
        this.on(event, wrapper);
    }
}

ES6小知识(??=)

逻辑运算符 ||= ??= &&= 都是一样的原理

const option = {}
function example(opts) {
    //不存在则赋值
  opts.foo ??= 'bar';
}
example(option)
//option {foo: 'bar'}
//等价于
//opts.foo = opts.foo ?? 'bar';
//opts.foo ?? (opts.foo = 'foo');