NestJS global-hook
前言
NestJs,我第一个使用的后端框架。从我开始工作的时候,我慢慢学会的。NestJS 不像是 express 或者 fastify 那样底层,更像是一个’中间件’,底层和上层部分都可以随意替换。如果你知道LLVM并基本了解LLVM的架构,这很像。
言归正传,我在公司的第一个项目的生产版本马上就要做完了,期间我遇到很多挑战。
其中就包含主题——循环依赖
这是什么?它是两个模块互相导入对方,就像是两条线头尾连了起来:没有出口了。
这个问题存在于一众编程语言中,并成为新手多个热门棘手问题中的其中一个。
问题
我一开始使用了回调,比如说你有两个模块,A和B。
A 依赖了 B,然后A会注册一个回调在B。
this.bService.registerOnDeleteCallback(() => {
console.log("It is deleted");
this.onDeleted();
}
);
当某个事件发生时,模块 B 会通知模块 A,这保证了基本的内聚——每个模块只干自己的事情,我们不希望模块B直接对模块A的数据进行原始操作。
使用回调的办法很好,但是仅限于你只有少数几个。但是我编写这篇文章时已经有5个类似的实现了,绝对不行!
隐性耦合是一个你平时不易察觉的怪物,回调基于特定模块,意味着模块缺失了之后将会导致注册回调的模块出错。
你可能会说:这不是废话吗? 但是先别接,让我们接着往下看。
解决方案
于是我做了一个 ‘GlobalHook’ 模块专门处理回调,然后我把所有类似的代码都转为了对这个模块的引用,以一种 ‘注册事件,然后发出事件’ 的模式。
这听起来很像是上世纪的 ‘接线员’ 现在模块A需要在这个模块注册回调,然后模块B在这个模块发出事件。
![]()
现在其它模块可以做一样的事了:给出一个唯一标识符,注册这个标识符,在这个标识符上发出事件。
import { Injectable } from '@nestjs/common';
import { randomUUID } from 'crypto';
class EventFunction {
callback: Function;
unique: string;
}
class EventNode {
event: string;
callbacks: EventFunction[];
}
@Injectable()
export class GlobalHookService {
constructor() {
const un = this.registerEvent('npmtest', (payload) => {
console.log("CB: ", payload);
})
console.log(un);
this.raiseEvent('npmtest', { a: 'what' });
this.removeEvent('npmtest', un as string);
this.raiseEvent('npmtest', { a: 'what' });
}
private _hooks: EventNode[] = [];
removeEvent(event: string, unique: string)
{
if(!this._hooks[event])
return false;
const callbacks = this._hooks[event].callbacks;
for(let i = 0; i < callbacks.length; ++i)
{
if(callbacks[i].unique === unique)
{
callbacks.splice(i, 1);
break;
}
}
return true;
}
registerEvent(event: string, callback: Function)
{
const ef: EventFunction = {
callback,
unique: randomUUID()
};
if(!this._hooks[event])
{
const en: EventNode = {
event,
callbacks: [ef]
};
this._hooks[event] = en;
return ef.unique;
}
this._hooks[event].callbacks.push(ef);
return ef.unique;
}
raiseEvent(event: string, payload: any)
{
if(!this._hooks[event])
return;
const callbacks = this._hooks[event].callbacks;
for(const c of callbacks)
{
c.callback(payload);
}
}
}
真不错!
编辑:实际上,在我后面迁移博客的时候,我发现这个概念早就存在了!它叫 EventBus 。好吧,我不清楚为什么我更喜欢叫 Hook 一点,但是这两个的确有一点像