In this blog post, we will look at how using async / await
simplified an Auth0 Webtask serverless function.
The idea.
In a previous post I used a serverless platform (Auth0 Webtask) to save money by watching @FreedomeVPN twitter feed for discount on their VPN service. That was a great node Js learning opportunity for me. However, it left me with a desire to try async/await
in node Js which looked more familiar to me coming from the C#
stack.
Since Webtask now runs on Node 8, support for async/await
is available. So let’s use it.
Note: you can find the full source here
Step 1, identify what would benefit from async/await
Basically my function does 4 things:
- Looks up the built-in Webtask storage for the Id of the last processed tweet
- Makes an
https
request to the Twitter api (usingaxios
http module from NPM) - Sends an email using
sendgrid
if a discount on FreeDomeVPN was found in a tweet - Updates the storage with the last processed tweet.
Everthing here is an async operation that was previously handled with callbacks (which are REALLY not as nice as async/await
)
Step 2, let’s dig deeper into the storage situation
Webtask built-in storage api doesn’t support async/await
natively (that I know of anyways), so let’s wrap calls to read and update storage.
Since async/await
is really using Promises
, I wrote this:
async function getLastTweetId(storage) {
return new Promise(resolve => {
storage.get(function (error, data) {
resolve(data.lastTweetId);
});
});
}
which I invoke by passing the storage
object supplied by the platform:
const lastTweetId = await getLastTweetId(context.storage);
This might not seem like a big improvement but you’ll see later why it is. I also applied the same principle to the Update operation on the storage
Step 3, Make the https request to the twitter API use async/await
too
This one is super straightforward since Axios already supports async/await
.
const response = await axios.get(twitterUrl, {
headers: {
Authorization: 'Bearer ' + context.secrets.TWITTER_API_KEY
}
});
Step 4, putting it all together
Now that we have small async
functions that can be called in a sequence, we just need to piece them together and not worry too much about the fact that they run asynchronously. We do that with one nice try/catch
block
try {
const lastTweetId = await getLastTweetId(context.storage);
var twitterUrl = 'https://api.twitter.com/1.1/statuses/...';
<... few lines removed for brevity>
const response = await axios.get(twitterUrl, { headers: { Authorization: '...' });
<... few lines removed for brevity>
await sendEmail(context, promoTweets[0]);
await setLastTweetId(context.storage, tweetsToInspect[0].id_str);
cb(null, "All done");
} catch (error) {
cb(error);
}
The fact that the main flow can be read on the same indentation line feels so much nicer to me.
Conclusion / Lessons learned / What’s next
For the same workload, using async/await
resulted in:
- Reduction of indentation by 3 (5 in the
async/await
way vs 8 in thecallback
way)
async/await
is great. Feels better to me than callback ‘hell’ and getting tangled up in braces.
Top image is from https://www.twilio.com/blog/2015/10/asyncawait-the-hero-javascript-deserved.html
Subscribe via RSS