A little lifehack for handling async/await requests in JavaScript
Before I start…
Hello readers (if someone reads it at all). Today I want to talk about async/await
requests in JavaScript.
Earlier I was against async/await
and considered it a bad replacement for Promises
. Because async/await
forces us to wrap async calls in try/catch
statement. And this is really ugly.
But now I understood my mistake and realized how to use this feature correctly (in my opinion).
So, let’s go deeper
Many people use try/catch
in every async/await
call. But I propose a more beautiful solution. Just wrap it in function that will not reject, but return an object with success flag.
Here I wrote a simple function that I use for fetch
queries:
async function query (url, options = {}) {
let response = null
try {
response = await fetch(url, options)
if (!response.ok) {
throw new Error('Failed to fetch')
}
return {
success: true,
response,
data: await response.json()
}
} catch (error) {
return {
success: false,
error,
response
}
}
}
Of course, in my code I use a more complex function that works with my API, but for example I decided to simplify that.
And here is an example of usage:
async function foo () {
let result = await query('/foo/bar')
if (result.success) {
console.log('Done', result.data)
// And here u have access to full response (result.response)
} else {
console.log('Fail', result.error)
// Here u also have full respones (result.response)
}
}
You’ll immediately ask me: “What is the advantage of this solution?”. The answer is simple. You can’t skip catch statement, but you can skip unnecessary part of condition. You also can remove unnecessary nesting with return
async function foo () {
let result = await query('/foo/bar')
if (!result.success) {
console.log('Fail', result.error)
return
}
console.log('Done', result.data)
}
And you can remove first nesting by wrapping it in a callback
function handleError (result) {
console.log('Fail', result.error)
}async function foo () {
let result = await query('/foo/bar')
if (!result.success) return handleError(result)
console.log('Done', result.data)
}
And finally…
I am a big fan of functional programming and I wrote a one more function…
function asyncWrapper (callback) {
return async function (...args) {
try {
return {
success: true,
result: await callback(...args)
}
} catch (reason) {
return {
success: false,
reason
}
}
}
}
… that wraps callback in try/catch
statement and returns result without reject:
let result = await asyncWrapper(fetch)('/foo/bar')
console.log(result) // { success: true, result: Body }
Thanks for reading. So, what do you think about this? Is it better than try/catch
or not? Please write in comments