How to Handle Unhandled promise rejection in async object method in Node.js expressjs?
Asked Answered
B

1

8

Im using the async function inside the object to send a response in express.js

Controller Code :

module.exports = {

    async signUpEmail(req, res) {

        /**
         * @description Parameters from body
         * @param {string} firstName - First Name
         * @inner
         */  

        const firstName = req.body.firstName;

        res.send({ success: name });
        throw new Error(); // purposely Done
    }
}

Question:

Since the signUpEmail method is async in my case and it will get rejected with whatever my async method throw's here it's comes Error.(purposely put there)

so getting this logged in the console.

(node:13537) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error
(node:13537) DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

So i'm supposed to handle it from the routes from where i'm calling it.

Router Code

    const routes = require('express').Router();
const SignUpController = require('../controllers/signUpController')

// /signup
routes.post('/', SignUpController.signUpEmail);

module.exports = routes;

some what like this SignUpController.signUpEmail().then(…); But since i'm not calling function in the routes i'm just passing. How this can be done effectively ?

PS:Please Don't suggest too complicated solutions. I'm beginner with JS and is learning through.

I Didn't use chainable route handlers because i want to create modular, mountable route handler.

Official Doc Example

Brisling answered 22/4, 2017 at 20:25 Comment(7)
Just pass a function that does: routes.pos('/', (...args) => SignUpController.signUpEmail().then(…))Himyaritic
@Himyaritic getting this now TypeError: Cannot read property 'body' of undefined. seems spreads not passing req ..tried console.log getting undefinedBrisling
Guess you'll want …signUpEmail(...args)… or whatever your function expectsHimyaritic
@Himyaritic yes got it figured.. if You could give a little enlightenment why this way its works especially how (...args) => gets available and what is this ? Because if i do` routes.post('/', console.log(args));` i get this ReferenceError: args is not defined would be very much helpfulBrisling
You have to put it inside the arrow function to log it. But you can also write it without spread parameters: routes.post('/', (req, res) => SignUpController.signUpEmail(req, res).then(…))Himyaritic
@Himyaritic Now i think i got it (spread) made it more clear .. was not aware of js spread still learning JS . so Basically Route.post() requires callback .. and we are doing the same resolving or rejecting the promise returned by this SignUpController.signUpEmail(req, res) and making the result as returned by normal function .. Correct me if i didn't understand.. Thanks for your great help :)Brisling
FYI, async/await is not part of ES7, it's part of ES2017.Pithos
L
8

In your route, you will need to add a wrapper to catch thrown errors:

let wrapper = fn => (...args) => fn(...args).catch(args[2]);

// /signup
routes.post('/', wrapper(SignUpController.signUpEmail));

With this method you can use a top level error catcher and do not need to use internal try catch blocks in your routes, unless you need to them contextually.

Use error catching middleware to achieve this as follows:

// last middleware in chain

app.use(function(err, req, res, next) {
    // handle your errors
});

Now in your routes you can throw errors and they will be caught by that middleware. I like to throw custom errors and handle their response and logging in this middleware.

Aside: The async await pattern is great for writing asynchronous code that is easy for a human to read in a synchronous fashion. Just remember that once you go async, you should stay async! Using a promisification library such as Bluebird and invoking .promisifyAll on nodeback libraries is highly recommended.

EDIT : Source - Asynchronous Error Handling in Express with Promises, Generators and ES7

Lung answered 22/4, 2017 at 20:30 Comment(3)
First one was of great help and understood it, but for using middleware doesn't we have pass next(err) to it somehow to work here ?Brisling
let wrapper = fn => (...args) => fn(...args).catch(args[2]); catches the thrown error from the function inside the wrapper and returns it as the route's next, which is then picked up by your error handling middlewareLung
Does this answer your question?Lung

© 2022 - 2024 — McMap. All rights reserved.