How to use single catch function for nested async await?

I have this code:

on('connection', async(socket) => {
  try {
    const promises = members.map(async(member) => {
      try {
        const users = await User.find({})
        const _promises = users.map(async() => {
          try {
            //some code here
          } catch (err) {
            console.log(err)
          }
        })
        await Promise.all(_promises)
      } catch (err) {
        console.log(err)
      }
    })
    await Promise.all(promises)
  } catch (err) {
    console.log(err)
    throw err
  }
})

As you can see I have a try catch for each nested async function. Would it be possible to tweak the code to use only a single catch, or to simplify things somehow?

2 answers

  • answered 2019-05-21 07:32 CertainPerformance

    You can have just a single await at the top level of the handler. If the Promises spawned inside it all chain together to a Promise which is awaited at the top level (like in your example), that top await will catch errors thrown anywhere inside.

    But, your catch section should not throw another error unless .on handles Promise rejections as well, otherwise you'll get an unhandled Promise rejection:

    on('connection', async(socket) => {
      try {
        const promises = members.map(async(member) => {
          const users = await User.find({})
          const _promises = users.map(async() => {
            //some code here which may throw
          });
          await Promise.all(_promises);
        });
        await Promise.all(promises);
      } catch (err) {
        console.log(err);
      }
    })
    
    • If await User.find throws, then the promises array will contain a Promise which rejects, which mean that the top await Promise.all(promises); will throw and be caught
    • If something inside users.map throws, _promises will contain a Promise which rejects, so await Promise.all(_promises); will throw. But that's inside the const promises = members.map callback, so that will result in promises containing a rejected Promise too - so it will be caught by the await Promise.all(promises); as well.

  • answered 2019-05-21 07:37 Charlie H

    If a function you call throws an error, the error will fall back to the nearest catch block the function call is enclosed in.

    try {
    
       throw "an error";
    
    }
    catch(e) {
       console.log(e);   //output: "an error"
    }
    

    Now consider this

    try {
    
      try {
    
       throw "an error";
    
      }
      catch(e) {
    
        console.log(e);   //output: "an error"
    
      }
    
    }
    catch(e) {
       console.log(e);   //This line is not going to be executed
    }
    

    This mechanism enables attachment of more error information to the generated error in each level. Imagine your error is an object and each nested catch bock attaching its own information to the error object an pass it along by throwing again.

    Look at the following code:

    try {

      try {
    
       throw {internalError: 101};
    
      }
      catch(e) {
    
        //Attach more info and throw again 
        e.additionalInfo = 'Disconnected when gathering information';
        throw e;
    
      }
    
    }
    catch(e) {
       e.message = 'Cannot get information'
       console.log(e);   //Final error with lot of information
    }