Reputation: 853
I used an EventEmitter as the following:
const EventEmitter = require('events');
const myEmitter = new EventEmitter();
function c2(num) {
return new Promise((resolve) => {
resolve(`c2: ${num}`);
});
}
// eslint-disable-next-line no-underscore-dangle
// eslint-disable-next-line no-console
const doSomeStuff = async (number) => {
try {
console.log(`doSomeStuff: ${number}`);
const r2 = await c2(number);
console.log(r2);
} catch (err) {
throw err;
}
};
myEmitter.on('eventOne', async (n) => {
await doSomeStuff(n);
});
myEmitter.emit('eventOne', 1);
myEmitter.emit('eventOne', 2);
myEmitter.emit('eventOne', 3);
myEmitter.emit('eventOne', 4);
I expect a result of
doSomeStuff: 1
c2: 1
doSomeStuff: 2
c2: 2
doSomeStuff: 3
c2: 3
doSomeStuff: 4
c2: 4
However the output shows me:
doSomeStuff: 1
doSomeStuff: 2
doSomeStuff: 3
doSomeStuff: 4
c2: 1
c2: 2
c2: 3
c2: 4
As per my understanding, EventEmitter
invokes the event callback function synchronously, however for some reason the callback function has not finished executing before the next callback function is invoked. I think I'm missing something very fundamental here.
Upvotes: 1
Views: 405
Reputation: 26867
The event handler doesn't care about the async
nature of the function. In fact, it doesn't care about the return value at all. It will just call it as soon as it can whenever it hears an event and will keep firing every time it hears an event. It won't care if it is already running a function or not.
myEmitter.on('eventOne', async (n) => {
await doSomeStuff(n);
});
Is effectively the exact same as if you didn't have the async/await:
myEmitter.on('eventOne', (n) => {
doSomeStuff(n);
});
Hypothetically, you could adjust your code a little such that the you do get the output that you are expecting. However, you need to introduce a singular path context such that every emitter is affecting 1 thing instead of every event triggering its own instance of the doSomeStuff
. Here is an example using generator functions:
// EventEmitter Polyfill
class EventEmitter {
constructor() {this._listeners = new Map();}
on(e, cb) {this._listeners.set(e, [...(this._listeners.get(e) || []), cb]);}
emit(e, payload) {for (const listener of (this._listeners.get(e) || [])) listener(payload);}
}
const myEmitter = new EventEmitter();
function c2(num) {
return new Promise(resolve => {
resolve(`c2: ${num}`);
});
}
async function* doSomeStuff() {
while (true) {
try {
const number = yield;
console.log(`doSomeStuff: ${number}`);
const r2 = await c2(number);
console.log(r2);
} catch (err) {
throw err;
}
}
}
const someStuff = doSomeStuff();
someStuff.next(); // Start it
myEmitter.on("eventOne", n => {
someStuff.next(n);
});
myEmitter.emit("eventOne", 1);
myEmitter.emit("eventOne", 2);
myEmitter.emit("eventOne", 3);
myEmitter.emit("eventOne", 4);
Upvotes: 2
Reputation: 3643
following the logic above, here is my opinions:
//You emitted event asynchronous, so the listeners will be called asynchronous in [A] scope
myEmitter.emit('eventOne', 1); // all
myEmitter.emit('eventOne', 2); // of
myEmitter.emit('eventOne', 3); // us
myEmitter.emit('eventOne', 4); // start at almost the same time.
// In listener:
await doSomeStuff(n); // I run synchronously inside [event 1] scope, not [A] scope
await doSomeStuff(n); // I run synchronously inside [event 2] scope, not [A] scope
await doSomeStuff(n); // I run synchronously inside [event 3] scope, not [A] scope
await doSomeStuff(n); // I run synchronously inside [event 4] scope, not [A] scope
and the rest will be resolve asynchronously.
brief: events are emitted ASYNCHRONOUSLY so that no reason for listeners listen them SYNCHRONOUSLY.
Upvotes: 1