A combinator is a higher-order function that uses only function application and earlier defined combinators to define a result from its arguments.
So we have a combinator, asyncHandler
-
const asyncHandler = (fn) => (req, res, next) => Promise.resolve(fn(req, res, next)).catch(next)
And we have getBootcamps
-
const getBootcamps = asyncHandler(...)
So we fill in the definition of asyncHandler
-
const getBootcamps = (fn) => (req, res, next) =>
Promise.resolve(fn(req, res, next)).catch(next)
Where the first argument, fn
, is equal to -
async (req, res, next) => {
const bootcamps = await Bootcamp.find();
res.status(200).json({ success: true, count: bootcamps.length, data: bootcamps });
}
So that becomes -
const getBootcamps = (req, res, next) =>
Promise.resolve((async (req, res, next) => {
const bootcamps = await Bootcamp.find();
res.status(200).json({ success: true, count: bootcamps.length, data: bootcamps });
})(req, res, next)).catch(next)
So as asyncHandler
's name implies, it accepts a fn
which is treated as an async handler. fn
can return a promise and asyncHandler
will properly wrap it and automatically connect the next
callback to properly handle errors. Related: What do multiple arrow functions mean in JavaScript?
This is an effective combinator because it prevents you from having to write this try
-catch
boilerplate for every async handler you would need -
// without asyncHandler
async function getBootcamps (req, res, next) {
try {
const bootcamps = await Bootcamp.find()
res.status(200).json({ success: true, count: bootcamps.length, data: bootcamps })
}
catch (err) {
next(err)
}
}
Instead asyncHandler
allows you to focus on "success" path and automatically handles the "error" path for you -
// with asyncHandler
const getBootcamps = asyncHandler(async (req, res, next) => {
const bootcamps = await Bootcamp.find()
res.status(200).json({ success: true, count: bootcamps.length, data: bootcamps })
})