15

So i'm using express.js and looking into using async/await with node 7. Is there a way that I can still catch errors but get rid of the try/catch block? Perhaps a function wrapper? I'm not sure how this would actually execute the function's code and also call next(err).

exports.index = async function(req, res, next) {
  try {
    let user = await User.findOne().exec();

    res.status(200).json(user);
  } catch(err) {
    next(err);
  }
}

Something like this...?

function example() {
   // Implements try/catch block and then handles error.
}

exports.index = async example(req, res, next) {
  let user = await User.findOne().exec();
  res.status(200).json(user);
}

EDIT:

Something more similar to this:

var wrapper = function(f) {
    return function() {
        try {
            f.apply(this, arguments);
        } catch(e) {
            customErrorHandler(e)
        }
    }
}

This would somehow handle the try/catch block but doesn't work:

exports.index = wrapper(async example(req, res, next) {
  let user = await User.findOne().exec();
  res.status(200).json(user);
});

See Is there a way to add try-catch to every function in Javascript? for the non-async example.

Community
  • 1
  • 1
Neverlax
  • 365
  • 1
  • 4
  • 15
  • 1
    Just use express-promise – Bergi Dec 27 '16 at 18:05
  • 1
    You can't wrap an await without making the wrapping function be async. In this case, why not just use `.then()` and `.catch()`. What is `await` buying you. Error handling needs to be there. You can't just wish it away to some other function. – jfriend00 Dec 27 '16 at 18:08
  • I'm looking at ways to remove nested `then` statements when they may need to fork. The above code is just there as a demonstrative example. – Neverlax Dec 27 '16 at 18:15
  • 1
    @Neverlax - You need to be more specific what you're asking then. There need be no nested `.then()` handlers in what you have above - you can just chain if there's a need for more than one async operation to be sequenced. You can't hide error handling and trying to is a fool's errand. A big downfall of `await` is that people tend to just shortchange error handling whereas the need for it is more obvious with `.then()` and `.catch()`. You can still do it with `await`, but it needs `try/catch. Please use real-world code that is as complicated as what you're trying to ask about. – jfriend00 Dec 27 '16 at 18:50
  • @jfriend00 Appreciate the comment. I feel its clear when i ask `Is there a way that I can still catch errors but get rid of the try/catch block scaffold` – Neverlax Dec 27 '16 at 18:58
  • FYI, `async/await` is not part of ES7. – Felix Kling Dec 30 '16 at 17:43
  • There's a great article about this topic with examples here: https://medium.com/@Abazhenov/using-async-await-in-express-with-node-8-b8af872c0016 – omer May 21 '19 at 09:45

4 Answers4

18

Yes, you can easily write such a wrapper for asynchronous functions as well - just use async/await:

function wrapper(f) {
    return async function() {
//         ^^^^^
        try {
            return await f.apply(this, arguments);
//                 ^^^^^
        } catch(e) {
            customErrorHandler(e)
        }
    }
}

Or you use promises directly, like in this example that is more tailored to express (especially with the number of parameters):

function promiseWrapper(fn) {
    return (req, res, next) => {
         fn(req, res).catch(next);
    };
}
Bergi
  • 572,313
  • 128
  • 898
  • 1,281
  • 1
    This does work. Though, async needs to be applied to the returned function instead of the actual wrapper function. Could you make that edit so that i can mark it is the answer? thanks! – Neverlax Dec 27 '16 at 19:21
  • Yes, it's an arrow function, but a plain function would've worked as well. What is important is that they are formally declared parameters, i.e. `promiseWrapper(…).length == 3`, which express uses to distinguish handlers. – Bergi Dec 27 '16 at 19:55
  • Can you please explain this for me as I am very new to Express and Javascript and finding it hard to understand how this works. I did not get the fact that we are returning a function which is executing the original function (fn) but how does this execute in practice – ankshukla Feb 16 '22 at 17:31
  • @ankshukla "*the fact that we are returning a function which is executing the original function*" - it seems like you did get it :-) Not sure what else there is to say. You might want to learn about closures in general? Or are you not sure how `wrapper` is applied? – Bergi Feb 16 '22 at 17:48
1

A similar answer here hope can help you

const sthError = () => Promise.reject('sth error');

const test = opts => {
  return (async () => {

    // do sth
    await sthError();
    return 'ok';

  })().catch(err => {
    console.error(err); // error will be catched there 
  });
};

test().then(ret => {
  console.log(ret);
});
1

If someone prefers async/await and Express-specific approach, following snippet could be useful

export function asyncWrap(fn) {
  return async function wrappedFn(req, res, next) {
    try {
      await fn(req, res);
    } catch (err) {
      next(err);
    }
  };
}

it can be used in the router in the following way

customRouter.get('/', asyncWrap(customController.getCustomListHandler));
Željko Šević
  • 2,717
  • 1
  • 18
  • 19
1

So, async functions are actually promises, I came up with this solution:

const asyncWrapper = async promise => {
    try {
        return [null, await promise];
    } catch (err) {
        return [err];
    }
};

const yourAsyncMethod = params => new Promise((resolve, reject) => {
    resolve(params);
});

(async () => {
  // Wrap the executed async method but use the await outside
  const [error, result] = await asyncWrapper(yourAsyncMethod(1));
  
  if (error) {
    // Handle error
  }
  console.log(result);
})();
jmdiego
  • 514
  • 6
  • 15