Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

JavaScript Asynchronous Code in Express Asynchronous Code in Express Reduce Error Handling Code When Using Async/Await

I don't fully understand asyncHandler. Can someone clarify this?

Here's the function for reference:

function asyncHandler(callback) {
  return async (req, res, next) => {
    try {
      await callback(req, res, next);
    } catch (err) {
      res.render('error', { error: err });
    }
  }
}

I know that this function is called in the route and it serves to wrap the functions you pass into your route in a try...catch block. But there are a few things I don't get.

  1. Why does it have to return an asynchronous function? Why can't it just accept a callback function as a parameter and call it in the try block? Something like this:
function asyncHandler(callback, req, res) {
  try {
    callback(req, res);
  } catch (err) {
    res.render('error', { error: err });
  }
}

I'm confused by the function returning another function.

  1. If asyncHandler returns a function that uses async/await then why do you then still have to use the async and await keywords in the callback you pass to it in the route?
app.get('/', asyncHandler(async (req,res) => {
  const users = await getUsers();
  res.render('index', { title: "Users", users: users.users });
})); 

Again this feels unnecessary, though when I removed them it didn't work anymore. But I'm just totally confused as to why you would need to use the async and await keywords in the callback when they've already been used in the definition of the function. Hopefully that makes sense. I guess this whole part made little sense to me, even though I already took the Asynchronous Programming course. Any help understanding this would be really appreciated.

5 Answers

Noah Nordqvist
Noah Nordqvist
10,246 Points

Quite some time for this question to be answered, but I hope it might be useful anyway:

I could misunderstand the concept myself since I'm pretty new to the world of asynchrony. But here is my take on what's going down.

So, we've got ourselves an asyncHandler:

function asyncHandler(cb) {
  return async function(req, res, next) {
    try {
      await cb(req, res, next);
    } catch(err) {
      res.render("error", { error: err })
    }
  };
}

Which is essentially just an empty template for plopping a callback of your choosing into, with the parameters request and response. Whatever callback we put in here will be run asynchronously through a try-catch block, but that's not the confusing part.

Now, as far as I understand it, we could actually put pretty much anything we want as a callback. It does not need the keyword async itself. The reason you need async-await can be seen in the callback from the original app.get block:

app.get('/', asyncHandler(async function(req, res){
  const users = await getUsers();
  res.render("index", { title: "Users", users: users.users });
})); 

As you can see here, the callback runs a function called getUsers and assigns its return value to the constant users. Let's have a look at getUsers:

function getUsers(){
  return new Promise(function(resolve, reject) {
    fs.readFile("data.json", "utf-8", function(err, data){
      if (err)
        reject(err);
      else {
        const users = JSON.parse(data);
        resolve(users);
      }
    });
  });
}

Here, we can see that getUsers returns a Promise.

Since we only want to render the page after we've received the parsed data from getUsers, we want to use the await keyword when calling getUsers in our original app.get block. And, since we want to use the await keyword, we need to make the entire callback an async function first.

Going back to the asyncHandler itself. Just because our template here is wrapped up as an async, doesn't mean that the function we pass through implicitly does the same. To do so, the callback we pass in must itself also be an async.

Hope this helps! And, if I'm wrong, I hope someone will let me know, so I know what to do next time.

Stay safe,

I tried to write the asyncHandler function before it was explained and failed miserably. I watched the entire video and still wasn't fully understanding what I was writing. After looking at the errors in the terminal I realised that the app.get function requires a callback function so the asyncHandler must return a function. If you try and run the callback within the asyncHandler only you will it will return a promise. From further studying the terminal errors I saw that the returning function must be passed the req and res objects to use them in the function passed to the error handler. I hope some of this is useful as I found that by attempting to write the code and looking carefully at the errors you are able to understand these problems much easier.

Appreciate the points, it gives us more depth.

William Mead
William Mead
9,937 Points

One of the confusing things about async and await is that they look simple. They look like regular synchronous code, and it is easy to forget that they are not. Once one function is getting something asynchronously, every function down the line needs to also run asynchronously.

I think Noah's comment above is good. I'm just adding that I think it is helpful to think of it as a chain of events.

I don't think the next is necessary as the function calls should always call the response object.

Also in a little confusion as we are 'tidying' things up by adding several layers of abstraction with anonymous functions, with multiple asyncs and awaits. I've placed the callback, in the handler. Pointless normally but done so we see the code altogether again.

function asyncHandler() {
  return async (req, res, next) => {
    try {
      await (async (req, res, next) => { // 'await'-ing on that cb function we put in 
        const users = await getUsers();
        res.render('index', {title: "Users", users: users.users});
      })
    } catch(err) {
      res.render('error', {error: err});
    }
  }
}

app.get('/', asyncHandler());

Hope this shows we are almost back to the way we looked originally.

Full disclosure: when I did this the code continues to search. I'm unsure if this is because I have mistyped or this is something to do with using middleware and not invoking the next() statement.