Reputation: 31
I'm talking about this function:
const asyncHandler = (fn) => (req, res, next) => Promise.resolve(fn(req, res, next)).catch(next);
I am keen to understand step by step what is going on when we pass it this function:
getBootcamps = asyncHandler(async (req, res, next) => {
const bootcamps = await Bootcamp.find();
res.status(200).json({ success: true, count: bootcamps.length, data: bootcamps });
});
Upvotes: 2
Views: 1356
Reputation: 135197
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 })
})
Upvotes: 1
Reputation: 2438
It generates a new function, based on existing function fn
, with the signature:
(req, res, next)
This generated function's body is:
return Promise.resolve(fn(req, res, next)).catch(next)
What it does is:
fn
(the underlying handler) with arguments (req, res, next)
fn
call to Promise.resolve() - this converts it to a Promise if it wasn't one alreadynext
) for when the promise rejectsThe code that you've posted seems to be an Adapter that converts an async function to work with next(err)
-based error handling (most likely for the express
framework). Notably, however, it only seems to care about the error case - it is still the async function's responsibility to send the response, thus ending the request processing.
Some other Web frameworks exist that make this step unnecessary - for instance, fastify can just use async functions as handlers directly and simply return the response object instead of doing an explicit .json()
call (which does res.end()
under the hood).
Upvotes: 2