在TypeScript中创建类内自定义事件

5
我将尝试解释如何在TypeScript中为类创建自定义事件。例如这个例子并没有很好地帮助我理解如何做到这一点。
下面是我要翻译的内容:

我的示例类看起来像这样。

Cat.ts:

export class Cat {
    public getName(): string {
        return this.catName;
    }

    public setName(catName: string) {
        this.catName = catName;
    }

    constructor(private catName: string) { }

    public makeMeow() {
        this.onWillMeow();
        console.log("Cat meows!");
        this.onDidMeow();
    }

    public onWillMeow() {
        console.log("onWillMeow");
    }

    public onDidMeow() {
        console.log("onDidMeow");
    }
}

现在我希望能够从外部声明事件,就像以下代码所示的那样。
const myCat: Cat = new Cat("Tikki");
myCat.onWillMeow({event => {
     console.log("Tikki the cat is just about to meow!");
}});
myCat.onWillMeow({event => {
     console.log("Tikki the cat did just meow!");
}});
myCat.makeMeow();

现在,我想要获得类似于以下输出:

onWillMeow
Tikki the cat is just about to meow!
Cat meows!
onDidMeow
Tikki the cat did just meow!

我需要做什么才能让这段代码在TypeScript中工作?这个叫什么?创建一个自定义事件还是创建自定义事件处理程序?
2个回答

12

类似这样的东西:

type Handler<E> = (event: E) => void;

class EventDispatcher<E> { 
    private handlers: Handler<E>[] = [];
    fire(event: E) { 
        for (let h of this.handlers)
            h(event);
    }
    register(handler: Handler<E>) { 
        this.handlers.push(handler);
    }
}

interface WillMeowEvent { }
interface DidMeowEvent { }

class Cat {
    public getName(): string {
        return this.catName;
    }

    public setName(catName: string) {
        this.catName = catName;
    }

    constructor(private catName: string) { }

    public makeMeow() {
        this.fireWillMeow({});
        console.log("Cat meows!");
        this.fireDidMeow({});
    }

    private willMeowDispatcher = new EventDispatcher<WillMeowEvent>();
    public onWillMeow(handler: Handler<WillMeowEvent>) {
        this.willMeowDispatcher.register(handler);
    }
    private fireWillMeow(event: WillMeowEvent) { 
        console.log("onWillMeow");
        this.willMeowDispatcher.fire(event);
    }

    private didMeowDispatcher = new EventDispatcher<DidMeowEvent>();
    public onDidMeow(handler: Handler<DidMeowEvent>) {
        this.didMeowDispatcher.register(handler);
    }
    private fireDidMeow(event: DidMeowEvent) { 
        console.log("onDidMeow");
        this.didMeowDispatcher.fire(event);
    }
}

const myCat: Cat = new Cat("Tikki");
myCat.onWillMeow(event => {
     console.log("Tikki the cat is just about to meow!");
});
myCat.onDidMeow(event => {
     console.log("Tikki the cat did just meow!");
});
myCat.makeMeow();

我相信有一些库可以帮忙。是否有人可以在另一个答案中推荐一个库呢?


谢谢您的回答。您能评估一下 type Handler<E> = (event: E) => void; 的含义吗?为什么指向 voidHandler 只是一个名称,可以随便取,对吗? - Socrates
1
void 表示事件处理程序(例如,带有 console.log("Tikki the cat is just about to meow!"); 的函数)不返回值。实际上,Handler 只是一个可以更改的名称,但它是一个相当标准的术语。 - Matt McCutchen
我相信有一些库可以帮助。有人想在另一个答案中推荐一个库吗?是的,而且不仅仅是给出一个链接,我还添加了一个完整的使用sub-events实现原始问题所需功能的示例。 - vitaly-t

2

在实现自定义事件时,重要的是事件是强类型的,即事件发送的数据类型在编译时推断,以使代码更少出错。

今天有几个库可以做到这一点。以下是使用 sub-events 的方法:

import {SubEvent} from 'sub-events';

class Cat {

    // strongly-typed event that expects a string:
    readonly onMeow: SubEvent<string> = new SubEvent();

    constructor(private catName: string) {
    }

    public makeMeow(message: string) {
        this.onMeow.emit(`${this.catName} ${message}`);
    }
}

const myCat = new Cat('Tikki');

myCat.onMeow.subscribe(message => {
    // message is strongly-typed here;

    console.log(message); //-> Tikki cat is doing something
});

myCat.makeMeow('cat is doing something'); // send the event

由于您发送的是仅消息不同的相同类型事件,因此通常会将其归纳为单个事件。这就是为什么在示例中我将其简化为一个事件onMeow

另外,subscribe基本上是今天的标准方法,让您随时轻松取消订阅,如下所示:

const sub = myCat.onMeow.subscribe(message => {
    console.log(message); // message is strongly-typed here;
});

当事件不再需要时,您取消订阅:
sub.cancel();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接