Error Handling and async-await

Eliya Cohen
3 min readJun 24, 2020

I would like to start this post by asking you the following question:

What does this piece of code:

async function asyncCall() {    setLoading(true);    const response = await getApiRequest();    setLoading(false);    return response.data;}

have in common with a never-ending spinner?

“It’s probably still loading” — Said no one ever

During a typical day at work, sometimes I go over other people’s code for inspiration (not really). Over time, I find myself stumbling across async-await syntax, and barely Promise..then syntax. Don’t get me wrong, I’m not against async-await syntax. But, when we usually use the await syntax, we’re often not devoting enough time handling errors that might pop out. I’ll give you an example — When I asked my co-worker, “What if the promise will fail?” he responded, “It’s okay, I wrote the server-side. It should never fail. Guess what? Two days after, someone else touched the server code, and we got a blank screen on the demo with a client. I’ll give you another example — When I asked a different co-worker, “Why not just use Promise..then for this scenario?” then he responded, “Well… it looks better like that”.

So, the answer to my question is — Unhandled promise rejection.

I’m not against using async-await as long as we take into consideration that a promise will fail at some point. There are few ways of handling these rejections cleanly, and I’ll show some of them:

1. Promise…then…catch

Simply replace the await with the “good-old” syntax which allows you to handle promise rejections out of the box:

function asyncCall() {
setLoading(true);
return getApiRequest()
.then(response => response.data)
.catch(response => showErrorNotification(response)
.finally(() => setLoading(false);
}

2. Wrap inside an async handler function

This is a custom solution which I found quite useful. Lately, I found out about react-query, which pretty much supports this kind of solution and even complicated:

function handleAsync(promise) {
return promise
.then(data => ({data, error: null}))
.catch(error => ({data: null, error}))
}
...function asyncCall() { setLoading(true); const { data, error } = await handleAsync(getApiRequest()); setLoading(false); if (error !== null) {
showErrorNotification(error);
}
return data
}

3. Try…catch

I’m usually not working with try/catch. Although it might be useful for some others:

async function asyncCall() {
setLoading(true);
try {
const response = await getApiRequest();
} catch (error) {
showErrorNotification(error);
} finally {
setLoading(false);
}
}

Conclusion

async-await has made our lives much easier. Although, it feels like we never take the time to actually capture the exceptions that might come from the awaited promises. Don’t let your customers wait for a never-ending spinner :)

--

--