Description
Just leaving this here as an issue worth investigating. Not sure there's anything we can do about it other than documenting it...
Multiple events emitted on the same process.nextTick()
may be missed when using await once()
. For example, when running the following, both the 'bar'
and 'foo'
events are emitted but the async function foo()
never completes because await once(myEE, 'foo')
occurs after the foo
event is actually emitted.
'use strict';
const { EventEmitter, once } = require('events');
const myEE = new EventEmitter();
async function foo() {
await once(myEE, 'bar');
console.log('bar');
await once(myEE, 'foo');
console.log('foo');
}
process.nextTick(() => {
myEE.emit('bar');
myEE.emit('foo');
});
foo().then(() => console.log('done'));
setTimeout(() => {}, 1000);
The only way to catch both events is to use Promise.all()
or Promise.allSettled()
, or to not use await
with once(myEE, 'bar')
'use strict';
const { EventEmitter, once } = require('events');
const myEE = new EventEmitter();
async function foo() {
await once(myEE, 'bar');
console.log('bar');
await once(myEE, 'foo');
console.log('foo');
}
async function foo2() {
await Promise.all([once(myEE, 'bar'), once(myEE, 'foo')]);
console.log('foo', 'bar');
}
process.nextTick(() => {
myEE.emit('bar');
myEE.emit('foo');
});
foo2().then(() => console.log('done'));
setTimeout(() => {}, 1000);
Why does this happen? It is because the process.nextTick is processed before the microtask queue. Both events are emitted synchronously before the microtask queue is processed. The await once(myEE, 'bar')
does not continue until the microtask queue is executed, after the 'foo'
event is emitted, so await once(myEE, 'foo')
ends up waiting forever, having missed the actual event.