6

Both try's print Promise { <pending> } and the second one has an Unhandled Promise Rejection Warning. I've had success just using Promises with .then and .catch, but there's some stuff I'd like to code in more of a synchronous way using async/await. Should I be using Yield instead perhaps?

try {
  var tokens = getNewTokens('refresh-token', 1)
  console.log(tokens)
} catch (error) {
  console.log(error.message)
}

try {
  tokens = getNewTokens('no-such-refresh-token', 1)
  console.log(tokens)
} catch (error) {
  console.log(error.message)
}

function getRefreshToken (refreshToken, userId) {
  return new Promise((resolve, reject) => {
    if (refreshToken === 'refresh-token' && userId === 1) {
      return resolve({
        refreshToken: 'refresh-token',
        accessToken: 'jwt'
      })
    } else {
      const error = Error('Refresh token not found')
      return reject(error)
    }
  })
}

async function getNewTokens (refreshToken, userId) {
  if (!refreshToken || !userId) {
    throw Error('Missing params')
  }
  try {
    var result = await getRefreshToken(refreshToken, userId)
  } catch (error) {
    throw Error(error.message)
  }
  if (!result) {
    throw Error('Something went bonk')
  }
  // Do stuff..
  // get user from DB
  // update refresh token with a new one
  // make new jwt
  return {
    user: {
      id: 1,
      name: 'Jerry',
      role: 'admin'
    },
    newRefreshToken: 'new-refresh-token',
    newAccessToken: 'new-jwt'
  }
}

3 Answers 3

24

Using async and await does not make your whole program asynchronous. It makes particular functions asynchronous. In the end, async is syntactical sugar that makes writing promise code easier.

An async function returns a Promise. The code that calls the asynchronous function must treat it as a function that returns a promise. (The only exception is when the code that is calling the async function is itself in an async function, in which case it can be awaited.)

So throwing an Error gets translated into a Promise rejection. You can't catch it in a try..catch block for the simple reason that the try..catch is over way before the error is thrown! (Again, the exception is when you're calling it from an async function. If you are awaiting an asynchronous function, a try..catch block will work. In this case the try..catch block is treated as if it were adding a Promise#catch function.)

You ultimately have to catch errors from an async function using the normal Promise#catch method or with the second argument to Promise#then:

getNewTokens('no-such-refresh-token', 1)
    .then(function(tokens) {
        console.log(tokens);
    }, function(error) {
        console.log(error.message);
    });
Sign up to request clarification or add additional context in comments.

Comments

13

I would like to submit this as a reasonable and readable way to handle errors using async/await.

const value = await asyncFunction().catch(err => new Error(err))
if (value instanceof Error) return res.status(500).send('There was an error doing something')

By adding the .catch() method, which returns an Error, thus assigning it to the value variable, we can be sure that some value will be present in our variable. The only additional line of code necessary is the if statement to test if the value is an instance of Error. If it was caught for any reason, it will definitely be an instanceof Error (our code makes sure of that), and our program can safely handle that scenario (in the above case we are returning 500 as a server response, thus preventing the program from executing any dangerous code as a result of the error in the asyncFunction).

I intentionally wrote this in 2 lines to highlight the fact that this can be written tersely and avoid the use of try/catch blocks, which many do not enjoy reading (myself being one).

If you're cool with reading try/catch blocks, congratulations. I just find it makes the code look janky. Oh well. ¯\_(ツ)_/¯

2 Comments

Thanks. Does this handle both network errors on the local device (for example, maybe the request was blocked by the browser on account of some extension) and errant responses from the server?
@raisinrising Contextually, this has nothing to do with the network. This is just the JavaScript runtime. If your JS function is making network calls, then it all depends on whether or not the internals of that function will treat those scenarios you mentioned as errors and throw them or as special return values and simply return them. You’ll have to experiment to find out.
7

A practical answer for beginners in async/await and error handling

You cannot use await syntax outside of a function that is not declared with async or is a promise.

async function myAsyncFunction() {
  // code
}

or

const myPromiseFunction = () => new Promise((resolve, reject) => {
    // code
})

So ultimately, you have to use the old Promise way to consume a function declared as async

myAsyncFunction().then(() => console.log('here'));

because both are Promises.

Now to actually handle errors like you are used to in the then-catch way, you have to construct your async function like so...

async function getUser(username) {
  return await db.users.get({ username });
}

then you can use this like a normal Promise outside of an async method like so...

getUser()
.then(user => console.log(user))
.catch(err => console.error(err));

or you guessed it, use it within an async function using await.

async function getJoffery() {
  return await getUser('joffery');
}

3 Comments

The first statement is not entirely correct. You can still await a function that doesn't have async in front of it so long as it returns a promise. You can also throw(err) inside the catch block instead of mixing Promise into an async function; this would be the more idiomatic approach since you're trying to demonstrate async/await here (with your example I wouldn't even use a catch block since you're just re-throwing the error).
Cool, I think the wording tripped me up before. These new examples are a little clearer with your intent and message.
@CalMlynarczyk yah I had this notion of try-catch wrong in async as well. I used RunJS locally and it deliberately throws like you suggested and the "catch" works as advertised. I do get node issues with that approach though as it borks for an uncaught exception at times so returning a Promise.reject eliminates that.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.