Code
Here is my solution for the problem 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)) ?? []
);
}
}
and some test code
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]
Here is the code in official TypeScriptPlayground
My Question
For the code in the current state output on my machine is this:
Output: [ 7 ]
in playground:
[LOG]:
[LOG]:
[LOG]: "Output:", [7]
As you can see, it outputs an array with 7 which is the correct output for this testcase
However, if I comment out the console.log()
(line 13 in playground) I get:
Output: []
on my machine or:
[LOG]: "Output:", []
in playground.
I do not understand, how can a single console.log()
change the behaviour of the program this much. I also do not know how to reproduce the issue in simpler code.
2
Answers
As was pointed out in the comments, the problem is that without
console.log()
both subscriptions happen "at the same time" so their timestamps are the same. So usingDate.now()
is most likely not a viable way to stamp callbacks. Here is an updated solution for the 2694 LeetCode problem that usesMath.random()
instead.The behavior you’re observing is related to the asynchronous nature of JavaScript and how the console.log() affects the timing of execution. The issue is not with the EventEmitter implementation itself but with how console.log() impacts the asynchronous execution of the code.
Let’s break down what’s happening step by step:
When you have the console.log() statement in the code (line 13), it introduces a slight delay in execution, which allows the "firstEvent" callbacks to be successfully registered before they are executed.
With the console.log() statement present, the "firstEvent" callbacks (subscribers) are correctly added to the
events
object, and when you callemitter.emit("firstEvent", [5])
, it triggers the callbacks, resulting in the correct output of[7]
.However, when you comment out the console.log() statement, the code executes faster since there is no delay introduced by the console.log(). This results in the "firstEvent" callbacks being registered and immediately removed (unsubscribed) before they have a chance to execute.
Let’s see the sequence of events without the console.log():
emitter.subscribe("firstEvent", (x) => x + 1)
, and it adds the callback toevents["firstEvent"]
array.emitter.subscribe("firstEvent", (x) => x + 2)
, and it adds the callback toevents["firstEvent"]
array.sub1.unsubscribe()
, which removes the first callback you added earlier from theevents["firstEvent"]
array.emitter.emit("firstEvent", [5])
, but since the second callback (x => x + 2) was registered and removed before the emit call, it won’t be executed, resulting in an empty array[]
.To address this issue, you might want to consider an alternative approach to delay the execution slightly, allowing the callbacks to be registered before the emit() call takes place. One way to do this is by using setTimeout() with a delay of 0:
By using setTimeout with a delay of 0, you ensure that the console.log() is executed asynchronously after the subscribe() function has finished its execution. This allows the callbacks to be registered before the emit() call takes place, giving you the correct output even without the console.log() statement.