1

I have a very simple route

router.get('/', async (req, res, next) => {
  try {
    const allEmployees = await employees.find({});
    res.json(allEmployees);
  } catch (error) {
    next(error);
  }
});

It works ok. But after I refactored it with catch. it stopped working and threw UnhandledPromiseRejectionWarning:

router.get('/', async (req, res, next) => {
  const allEmployees = await employees.find({}).catch(next)
  res.json(allEmployees);
});

It seems like the next is not called correctly in the second version. But Both should be equivalent in JavaScript. not sure why the second one is broken in Express.

Phil
  • 157,677
  • 23
  • 242
  • 245
Joji
  • 4,703
  • 7
  • 41
  • 86

1 Answers1

2

Both should be equivalent in JavaScript

No, they are not.

Promise.prototype.catch() returns a new promise that resolves with the return value of the callback. Since next() returns void, this code...

employees.find({}).catch(next)

returns a successful promise, resolving with undefined. next() will be called with the failure but there's nothing stopping the rest of your code from calling res.json(undefined).

If you want to use the Promise prototype methods, the equivalent would be

router.get('/', (req, res, next) => {
  employees.find({}).then(res.json).catch(next);
});

If you wanted to keep using async / await and have the eventual promise fail, you'd need something like this

router.get("/", async (req, res, next) => {
  const allEmployees = await employees
    .find({})
    .catch((err) => Promise.reject(next())); // rejected result
  res.json(allEmployees);
});
Phil
  • 157,677
  • 23
  • 242
  • 245
  • I know how `catch` works in JS. for this `.catch(next);`, it is equivalent of `.catch(e => next(e));` my understand is that once `next` gets called, it is effectively a return statement so `res.json(undefined)` won't be reached. – Joji Oct 09 '22 at 23:08
  • @Joji no. Because your `.catch()` returns a successful promise, the rest of your code continues as if everything is ok – Phil Oct 09 '22 at 23:10
  • yea I know it returns a successful promise. but `next` is indeed *called* inside of it with the `error` passed to it. so it should behave like a return statement at that point isn't it? – Joji Oct 09 '22 at 23:12
  • @Joji no, `next()` just passes the arg to the general error handler but it does not stop anything else from happening. – Phil Oct 09 '22 at 23:13
  • got it... so the recommended approach is to just use `try catch` with `await`? – Joji Oct 09 '22 at 23:15
  • @Joji that's correct. Express v5 has (will have) better error handling for async handlers. See https://expressjs.com/en/guide/error-handling.html#catching-errors – Phil Oct 09 '22 at 23:17