davidbanham / express-async-errors

async/await support for ExpressJS
Other
891 stars 42 forks source link

Can not handler event based error handler #42

Closed Rafat97 closed 1 year ago

Rafat97 commented 2 years ago

I created an express router. The main functionality of this router is to emit some events based on some of my business logic. Mainly, I tried to implement an event-driven architecture using events.EventEmitter(). My source code is-

import { ExpressApplicationRouter } from "@rafat97/express-made-easy";
import events from "events";

var eventEmitter = new events.EventEmitter();

var myEventErrorHandler = async function (req: any, res: any) {
  throw new Error("Test Error");
};

var myEventResponseHandler = async function (req: any, res: any) {
  res.send({ title: "login route" });
};

eventEmitter.on("event:error", myEventErrorHandler);
eventEmitter.on("event:response", myEventResponseHandler);

const loginRoutes = new ExpressApplicationRouter();
loginRoutes.getMethod("/", (req: any, res: any) => {
  setTimeout(() => {
    res.status(408).send({ message: "Request Timeout" });
  }, 10000);
  eventEmitter.emit("event:error", req, res);
});

export { loginRoutes };

In the console, I get this error message.

Error: Test Error
    at EventEmitter.<anonymous> (C:\Users\User\Documents\GitHub\nodejs-microservice\services\auth\src\routes\login.ts:7:9)
    at step (C:\Users\User\Documents\GitHub\nodejs-microservice\services\auth\src\routes\login.ts:33:23)
    at Object.next (C:\Users\User\Documents\GitHub\nodejs-microservice\services\auth\src\routes\login.ts:14:53)
    at C:\Users\User\Documents\GitHub\nodejs-microservice\services\auth\src\routes\login.ts:8:71
    at new Promise (<anonymous>)
    at __awaiter (C:\Users\User\Documents\GitHub\nodejs-microservice\services\auth\src\routes\login.ts:4:12)
    at EventEmitter.myEventErrorHandler (C:\Users\User\Documents\GitHub\nodejs-microservice\services\auth\src\routes\login.ts:47:12)
    at EventEmitter.emit (node:events:527:28)
    at C:\Users\User\Documents\GitHub\nodejs-microservice\services\auth\src\routes\login.ts:26:16
    at newFn (C:\Users\User\Documents\GitHub\nodejs-microservice\services\auth\node_modules\express-async-errors\index.js:16:20)
MatthewPinheiro commented 1 year ago

This is definitely expected behavior--not an issue with the library--because you're not awaiting the rejected promise that's returned by the call to myEventErrorHandler. As a result, there's no mechanism allowing the rejected promise to be handled.

In fact, you can see this same problem another way by replacing the line:

eventEmitter.emit("event:error", req, res);

with

myEventErrorHandler(); // NOT awaited, but see how the behavior changes when you do

With or without this library, you will still end up with the exact same error for the exact same reason--because you've allowed a rejected promise to sit unhandled until the end of the current iteration of the event loop. Only once you await a rejected promise does it become an exception which can be handled higher up the call stack.

However, there's a second issue. If you stuck with me this far, you might think that the solution is just to await the eventEmitter.emit(...) line, right? Unfortunately, no. The emit method of EventEmitters returns a boolean, not the return value of any listener function, meaning you can't await it. And that's for good reason. Not only can there be multiple listeners and thus multiple return values, but also because the purpose of an event-driven architecture is to separate the concerns of the publisher (i.e. the emitter) from the concerns of the subscriber(s) (i.e. the listener(s)). If there's an error in a listening function, that should be handled by the subscriber, not the publisher.