[node.js] try/catch blocks with async/await

I'm digging into the node 7 async/await feature and keep stumbling across code like this

_x000D_
_x000D_
function getQuote() {_x000D_
  let quote = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";_x000D_
  return quote;_x000D_
}_x000D_
_x000D_
async function main() {_x000D_
  try {_x000D_
    var quote = await getQuote();_x000D_
    console.log(quote);_x000D_
  } catch (error) {_x000D_
    console.error(error);_x000D_
  }_x000D_
}_x000D_
_x000D_
main();
_x000D_
_x000D_
_x000D_

This seems to be the only possibility resolve/reject or return/throw with async/await, however, v8 doesn't optimise code within try/catch blocks?!

Are there alternatives?

This question is related to node.js async-await ecmascript-2017

The answer is


async function main() {
  var getQuoteError
  var quote = await getQuote().catch(err => { getQuoteError = err }

  if (getQuoteError) return console.error(err)

  console.log(quote)
}

Alternatively instead of declaring a possible var to hold an error at the top you can do

if (quote instanceof Error) {
  // ...
}

Though that won't work if something like a TypeError or Reference error is thrown. You can ensure it is a regular error though with

async function main() {
  var quote = await getQuote().catch(err => {
    console.error(err)      

    return new Error('Error getting quote')
  })

  if (quote instanceOf Error) return quote // get out of here or do whatever

  console.log(quote)
}

My preference for this is wrapping everything in a big try-catch block where there's multiple promises being created can make it cumbersome to handle the error specifically to the promise that created it. With the alternative being multiple try-catch blocks which I find equally cumbersome


An alternative to try-catch block is await-to-js lib. I often use it. For example:

import to from 'await-to-js';

async function main(callback) {
    const [err,quote] = await to(getQuote());
    
    if(err || !quote) return callback(new Error('No Quote found'));

    callback(null,quote);

}

This syntax is much cleaner when compared to try-catch.


catching in this fashion, in my experience, is dangerous. Any error thrown in the entire stack will be caught, not just an error from this promise (which is probably not what you want).

The second argument to a promise is already a rejection/failure callback. It's better and safer to use that instead.

Here's a typescript typesafe one-liner I wrote to handle this:

function wait<R, E>(promise: Promise<R>): [R | null, E | null] {
  return (promise.then((data: R) => [data, null], (err: E) => [null, err]) as any) as [R, E];
}

// Usage
const [currUser, currUserError] = await wait<GetCurrentUser_user, GetCurrentUser_errors>(
  apiClient.getCurrentUser()
);

I'd like to do this way :)

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

It's similar to handling error with co

const test = opts => {
  return co(function*() {

    // do sth
    yield sthError();
    return 'ok';

  }).catch(err => {
    console.error(err);
  });
};

A cleaner alternative would be the following:

Due to the fact that every async function is technically a promise

You can add catches to functions when calling them with await

async function a(){
    let error;

    // log the error on the parent
    await b().catch((err)=>console.log('b.failed'))

    // change an error variable
    await c().catch((err)=>{error=true; console.log(err)})

    // return whatever you want
    return error ? d() : null;
}
a().catch(()=>console.log('main program failed'))

No need for try catch, as all promises errors are handled, and you have no code errors, you can omit that in the parent!!

Lets say you are working with mongodb, if there is an error you might prefer to handle it in the function calling it than making wrappers, or using try catches.


Alternative Similar To Error Handling In Golang

Because async/await uses promises under the hood, you can write a little utility function like this:

export function catchEm(promise) {
  return promise.then(data => [null, data])
    .catch(err => [err]);
}

Then import it whenever you need to catch some errors, and wrap your async function which returns a promise with it.

import catchEm from 'utility';

async performAsyncWork() {
  const [err, data] = await catchEm(asyncFunction(arg1, arg2));
  if (err) {
    // handle errors
  } else {
    // use data
  }
}

Examples related to node.js

Hide Signs that Meteor.js was Used Querying date field in MongoDB with Mongoose SyntaxError: Cannot use import statement outside a module Server Discovery And Monitoring engine is deprecated How to fix ReferenceError: primordials is not defined in node UnhandledPromiseRejectionWarning: This error originated either by throwing inside of an async function without a catch block dyld: Library not loaded: /usr/local/opt/icu4c/lib/libicui18n.62.dylib error running php after installing node with brew on Mac internal/modules/cjs/loader.js:582 throw err DeprecationWarning: Buffer() is deprecated due to security and usability issues when I move my script to another server Please run `npm cache clean`

Examples related to async-await

How to correctly write async method? How can I use async/await at the top level? Any difference between await Promise.all() and multiple await? Async/Await Class Constructor Syntax for async arrow function try/catch blocks with async/await Using filesystem in node.js with async / await Use async await with Array.map Using await outside of an async function SyntaxError: Unexpected token function - Async Await Nodejs

Examples related to ecmascript-2017

Use Async/Await with Axios in React.js How can I use async/await at the top level? How to reject in async/await syntax? try/catch blocks with async/await Use async await with Array.map Using async/await with a forEach loop Combination of async function + await + setTimeout