From callback hell, through promises to async functions
Working with asynchronous JavaScript has changed a lot in the last few years. When Promise
s were introduced to ECMAScript a few years ago life became a dream. Two years later, the async
function was added to the specification — I still can’t believe how nice and easy to read it is! What’s next? Time will tell, but now let’s review what we went through to get where we are. You have to agree — a random Chuck Norris joke generator is the perfect demo program!
Callbacks
If you joined the JS stage a few years ago or earlier you’ll remember the doom that you could easily end up in by nesting callbacks. Luckily those days are over. A simple XMLHttpRequest
request fetches a piece of JSON from an API and it isn’t hard to read (yet). Imagine a situation where you have to call another request based on the result of the first one — I don’t miss that.
function getRandomJoke() {
// show loading screen
loading.classList.add('loading--active');
var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://api.icndb.com/jokes/random/1');
xhr.responseType = 'json';
xhr.onload = function onload() {
// print a joke
main.innerHTML = xhr.response.value[0].joke;
// hide loading screen
loading.classList.remove('loading--active');
};
xhr.onerror = function onerror() {
// print an error
main.textContent = 'Error :-(';
// hide loading screen
loading.classList.remove('loading--active');
};
xhr.send();
}
See the Pen 2017-10-24-1 by Pawel Grzybek (@pawelgrzybek) on CodePen.
The promise of a better future
An asynchronous operation takes some time and the only thing that we can be assured of ahead of time is the fact that it is going to be resolved or rejected. This could easily be the definition of a JavaScript Promise
. It landed in the ECMAScript spec in 2015 and totally revolutionized the way developers handle asynchronous programs. Promised-based methods (the ones that return the promise) are slowly replacing callback-based equivalents and fetch()
, used in example below, is one of them.
function getRandomJoke() {
// show loading screen
loading.classList.add('loading--active');
fetch('https://api.icndb.com/jokes/random/1')
.then(response => response.json())
.then(joke => {
// print a joke
main.innerHTML = joke.value[0].joke;
// hide loading screen
loading.classList.remove('loading--active');
})
.catch(err => {
// print an error
main.textContent = `Error: ${err}`;
// hide loading screen
loading.classList.remove('loading--active');
});
}
See the Pen 2017-10-24-2 by Pawel Grzybek (@pawelgrzybek) on CodePen.
Living on the bleeding edge? Safari 11, Google Chrome 63, EDGE and Node under the --harmony-promise-finally
flag comes with the additional handler finally()
. Keep it DRY!
function getRandomJoke() {
// show loading screen
loading.classList.add('loading--active');
fetch('https://api.icndb.com/jokes/random/1')
.then(response => response.json())
.then(joke => {
// print a joke
main.innerHTML = joke.value[0].joke;
})
.catch(err => {
// print an error
main.textContent = `Error: ${err}`;
})
.finally(() => {
// hide loading screen
loading.classList.remove('loading--active');
});
}
See the Pen 2017-10-24-3 by Pawel Grzybek (@pawelgrzybek) on CodePen.
Async code, sync look
In 2017 ECMAScript introduced another game changer — the Async Function. It makes working with asynchronous operations very intuitive because it is written in the same way as synchronous code with two tiny nuances — an async
keyword in front of a function declaration and an await
operator as a promise prefix. Apart from it just being syntactically much nicer to read, it comes with optimizations in JavaScript engines as well. Based on the current browser support and the help that babel-preset-env can offer this is my preferred way of working with asynchronous JavaScript.
const getRandomJoke = async() => {
// show loading screen
loading.classList.add('loading--active');
try {
const response = await fetch('https://api.icndb.com/jokes/random/1');
const joke = await response.json();
// print a joke
main.innerHTML = joke.value[0].joke;
}
catch (err) {
// print an error
main.textContent = `Error: ${err}`;
}
finally {
// hide loading screen
loading.classList.remove('loading--active');
}
};
See the Pen 2017-10-24-4 by Pawel Grzybek (@pawelgrzybek) on CodePen.
Hopefully you liked this quick time travel through asynchronous programming in JavaScript. The changes introduced in latest versions of the language are really exciting and I am really looking forward to see what’s next! Until next time pals!
I recently came across the async function when I was exploring promises in JavaScript. Great explanation of how it works, I was able to implement another version of this as well. Cheers :)
I'm glad that you like it. Have a good day @malikbrowne:disqus 🥑