代码
下面是我对问题**2694: EventEmitter**的解决方案:
type Callback = (...args: any[]) => any;
type Subscription = {
unsubscribe: () => void;
};
type StampedCallback = { callback: Callback; timestamp: number };
class EventEmitter {
events: Record<string, StampedCallback[]> = {};
subscribe(eventName: string, callback: Callback): Subscription {
if (!this.events[eventName]) this.events[eventName] = [];
const timestamp = Date.now();
this.events[eventName].push({ callback: callback, timestamp: timestamp });
// Pay attention to this console.log() here
console.log();
return {
unsubscribe: () => {
this.events[eventName] = this.events[eventName].filter(
(stampedCallback) => stampedCallback.timestamp !== timestamp,
);
},
};
}
emit(eventName: string, args: any[] = []): any[] {
return (
this.events[eventName]?.map(({ callback }) => callback(...args)) ?? []
);
}
}
字符串
和一些测试代码
const emitter = new EventEmitter();
const sub1 = emitter.subscribe("firstEvent", (x) => x + 1);
const sub2 = emitter.subscribe("firstEvent", (x) => x + 2);
sub1.unsubscribe(); // undefined
console.log("Output:", emitter.emit("firstEvent", [5])); // [7]
型
下面是官方TypeScriptPlayground中的代码
我的问题
对于我的机器上的当前状态输出的代码是这样的:
Output: [ 7 ]
型
在Playground:
[LOG]:
[LOG]:
[LOG]: "Output:", [7]
型
正如您所看到的,它输出了一个带有7的数组,这是这个测试用例的正确输出
但是,如果我注解掉console.log()
(playground中的第13行),我会得到:
Output: []
型
在我的机器上或:
[LOG]: "Output:", []
型
在操场上。我不明白,一个console.log()
怎么能改变程序的行为这么多。我也不知道如何用更简单的代码重现这个问题。
2条答案
按热度按时间nukf8bse1#
您观察到的行为与JavaScript的异步特性以及console.log()如何影响执行时间有关。问题不在于EventEmitter实现本身,而在于console.log()如何影响代码的异步执行。
让我们一步一步地分解正在发生的事情:
1.当代码中有console.log()语句时(第13行),它会在执行中引入一点延迟,这允许“firstEvent”回调在执行之前成功注册。
1.使用console.log()语句,“firstEvent”回调(订阅者)被正确地添加到
events
对象中,当您调用emitter.emit("firstEvent", [5])
时,它触发回调,导致[7]
的正确输出。1.但是,当注解掉console.log()语句时,代码执行得更快,因为console.log()没有引入延迟。这会导致“firstEvent”回调被注册,并在它们有机会执行之前立即删除(取消订阅)。
让我们看看没有console.log()的事件序列:
emitter.subscribe("firstEvent", (x) => x + 1)
,它将回调添加到events["firstEvent"]
数组。emitter.subscribe("firstEvent", (x) => x + 2)
,它会将回调函数添加到events["firstEvent"]
数组。sub1.unsubscribe()
,这将从events["firstEvent"]
数组中删除前面添加的第一个回调。emitter.emit("firstEvent", [5])
,但由于第二个回调(x => x + 2)在emit调用之前已注册并删除,因此不会执行,从而导致空数组[]
。要解决这个问题,您可能需要考虑一种替代方法来稍微延迟执行,允许在emit()调用发生之前注册回调。一种方法是使用setTimeout(),延迟为0:
字符串
通过使用延迟为0的setTimeout,可以确保console.log()在subscribe()函数执行完毕后异步执行。这允许在emit()调用发生之前注册回调,即使没有console.log()语句,也能提供正确的输出。
tyu7yeag2#
正如在评论中指出的那样,问题是如果没有
console.log()
,两个订阅“同时”发生,所以它们的时间戳是相同的。因此,使用Date.now()
很可能不是标记回调的可行方法。下面是更新后的**solution for the 2694 LeetCode problem**,它使用了Math.random()
。