Is there a way to wrap an await/async try/catch block to every function?

18.7k views Asked by At

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.

6

There are 6 answers

4
Bergi On BEST ANSWER

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);
    };
}
0
jmdiego On

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);
})();

0
Željko Šević On

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));
1
Yilmaz On
const catchAsyncErrors = (func) => (req, res, next) =>
  Promise.resolve(func(req, res, next)).catch((err) => {
    console.log("error", err);
    next();
  });

export default catchAsyncErrors;

then in controllers:

const currentUserProfile = catchAsyncErrors(async (req, res) => {
  const user = await User.findById(req.user._id);
  res.status(200).json({
    success: true,
    user,
  });
});
0
Kildareflare On

I'm using Firebase Functions onRequest method to serve an Express App. The other answers on this page helped me arrive at this solution for catching errors at the top level and avoid having to have a try/catch inside each handler.

The keypoints were

  • add async to the onRequest method
const admin = functions.https.onRequest(async (req, res) => adminApp(req, res));
// adminApp
const app = express();

// middleware here...

addPostHandler(app, '/createUser', createUser);
//  ...other routes

// error middleware here...
  • add await to the handler call
// addPostHandler
export const addPostHandler = (app, route: string, handler) => {
  app.post(route, async (req: express.Request, res: express.Response, next: express.NextFunction) => {
    // try catch here means we don't need to add inside each handler
    try {
      await handler(req, res);
    } catch (error) {
      return next(error);
    }
  });
};
0
Cooper Hsiung On

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);
});