Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rejection in async listener results in Node process crash #310

Closed
inga-lovinde opened this issue Mar 4, 2024 · 1 comment
Closed

Rejection in async listener results in Node process crash #310

inga-lovinde opened this issue Mar 4, 2024 · 1 comment

Comments

@inga-lovinde
Copy link

According to the documentation, one of the features of EventEmitter2 is that it supports async listeners. The documentation does not mention that listeners should never reject.

Rejections in listeners result in Node process crash. For example:

const EventEmitter2 = require('eventemitter2');

const rejects = () =>
  new Promise((resolve, reject) => {
    console.log('event listener called');
    reject('from async');
  });

const emitter = new EventEmitter2();

emitter.on('x', rejects, {
  promisify: true,
  async: true,
});
const emitted = emitter.emit('x');
console.log({ emitted });
setTimeout(() => console.log('success!'), 100);

produces the following output:

{ emitted: true }
event listener called
[UnhandledPromiseRejection: This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason "async error".] {
  code: 'ERR_UNHANDLED_REJECTION'
}

Replacing reject('from async') with resolve('from async') results in

{ emitted: true }
event listener called
success!

Changing promisify and async listener options does not seem to affect this.

It seems that EventEmitter2 does not handle any promise rejections (at least I cannot find any mentions of catch in the source code). Since the value returned by emit is simply true, it seems that there is no way for caller of emit to handle any rejections either.

So the only way to not crash Node process is to handle all rejections in the listener, and to use EventEmitter2, for example, like this:

const handleEvent = async (eventData) => {
  // do some async stuff, I/O, network calls, etc
}

const handleEventSafe = (eventData) => handleEvent(eventData).catch(e => console.error(e));

eventEmitter.on('myEvent', handleEventSafe);

This is a very significant caveat, and it should probably be mentioned in the documentation that async listeners should never ever throw or reject.

(This is not an issue with sync listeners, because errors thrown by sync listeners can be caught by caller of emit as ordinary sync errors.)

@inga-lovinde
Copy link
Author

inga-lovinde commented Mar 4, 2024

This is probably a duplicate of #298, I didn't notice it at first because I was searching by "reject" keyword.

Still, the documentation should probably mention that only emitAsync (the one that returns promises from listeners, allowing the caller to handle rejections) should be used in conjunction with async listeners.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant