Stop trying to make async/await happen
Promises are so fetch right now
Since the beginning of time, developers have fought many silly fights. From the classic “Tabs vs. Spaces” to the timeless “Mac vs. PC” debate, we’re good at finding distracting arguments.
Answers: Linux & Spaces.
The Fight…?
Promises vs. Async/Await!
Wait, is this a fight? It must be right? We don’t seem to talk about callbacks anymore?
No, it’s not a fight. Ultimately it’s another potential tool in your toolbox. However, because async
/await
doesn’t replace all Promise functionality (specifically Promise.all
, .race
) it’s misleading presenting it as a replacement.
There’s a lot of influential people promoting this misconception async
/await
is the Promises replacement everyone’s been waiting for.
Hint: No, nope, and not even a little.
A recent addition to VS Code advances this bias. As @umaar tweeted:
Visual Studio Code can now convert your long chains of Promise.then()'s into async/await! 🎊 Works very well in both JavaScript and TypeScript files. .catch() is also correctly converted to try/catch ✅ pic.twitter.com/xb39Lsp84V
— Umar Hansa (@umaar) September 28, 2018
If you hate Promises, and want this refactoring feature, I don’t blame you.
I empathize. I understand.
I’ve been there. 🤗
I used to hate Promises. Today, I have come back around completely. Promises are amazing. They can enable/encourage you to take advantage of function composition.
There are 2 areas I recommend focusing on first to advance your Promise technique.
#1: Named Functions!
Kill your anonymous methods. Using named functions makes code read like poetry of your requirements.
Let’s look at a common example:
Making an HTTP GET request using fetch
:
Anti-Pattern
// ❌ Using anonymous inline functions 💩
fetch(url)
.then(response => response.status < 400
? response
: Promise.reject(new Error('Request Failed: ' + response.ststus)))
.then(response => response.text())
Solution: Named Methods
// ✅ Clarity emerges: named functions
fetch(url)
.then(checkResponse)
.then(getText)
// Reusable general-purpose functions
function checkResponse(response) {
return response.status < 400
? response
: Promise.reject(new Error('Request Failed: ' + response.ststus))
}
function getText(response) {
return response.text()
}
The benefits of this approach are increasingly apparent as you get DRY-er code.
Additional Resources: Check out my 1 minute videos of basic logging and advanced debugging using this technique.
#2: Single Purpose (Functions)
It sounds deceptively precise: Single Purpose.
Yet it’s so subjective, arbitrary, and sure, sometimes even meaningless.
Interestingly, most developers report they are pretty dang good at Single Purpose’ing their code. Not unrelated: they report being great drivers too!
Let’s look at an example the (incredibly talented) Jake Archibald features in his async/await article for the Google Developers site.
// source: https://developers.google.com/web/fundamentals/primers/async-functions
function logInOrder(urls) {
// fetch all the URLs
const textPromises = urls.map(url => {
return fetch(url).then(response => response.text());
});
// log them in order
textPromises.reduce((chain, textPromise) => {
return chain.then(() => textPromise)
.then(text => console.log(text));
}, Promise.resolve());
}
Single Purpose?
I’d say no. What’s logInOrder
doing?
- loop through a list of
urls
- apply them to an inline HTTP GET:
- HTTP
fetch
- return response text body
- append a
.then(text => console.log(text))
after each promise intextPromise
- print results serially
There are 5 anonymous methods defined in this single function. As Jake even points out, the .reduce
is too complex. It doesn’t make sense to hand-write nuanced mechanisms all over your code. Put another way, we don’t write DOM creation code with endless document.createElement()
, element.setAttribute()
, etc. Instead we choose the best tool out of many options: helper/utility functions, libraries or frameworks.
Solution: Single Purpose Functions
Begin by extracting methods…
Continue by replacing the .reduce()
and logPromise()
with a Promise.all
and a ..map()
…
Summary
Try apply these techniques to your own code! Then tweet at me & let me know how it went. Or if you have questions or comments, reach out as well!
Help spread the #PromiseTruth & share this article. ❤️