The website content provides an overview of asynchronous code handling in JavaScript through the use of callbacks, promises, and async/await syntax, emphasizing the evolution towards cleaner and more readable code.
Abstract
The article discusses the challenges of executing asynchronous operations in JavaScript, initially demonstrating how using setTimeout without proper control leads to unpredictable execution order. It then proceeds to illustrate how callbacks can be used to manage the order of asynchronous function calls, despite the potential for creating deeply nested and difficult-to-read "Callback Hell." The article introduces Promises as a solution to the nesting problem, showcasing how they allow for cleaner promise chains and the passing of data through asynchronous operations. Finally, it presents async/await as a more intuitive and synchronous-like approach to handle promises, significantly improving code readability and maintainability.
Opinions
The author suggests that arrow functions enhance code readability by providing a more concise syntax for anonymous functions.
The article implies that using callbacks alone can lead to complex and unmanageable code, known as "Callback Hell."
Promises are presented as a significant improvement over callbacks, offering a clearer structure and better handling of asynchronous results.
The author expresses a clear preference for async/await syntax over callbacks and promises due to its resemblance to synchronous code, making it easier to understand and work with.
The article conveys that each successive method of handling asynchronous code (callbacks, promises, async/await) is an attempt to improve upon the limitations of the previous one.
Callbacks, Promises and Async/Await
This is my attempt to explain how to use asynchronous code in Javascript at a very high level.
Note: I’m going to be using arrow functions in this blog post. Basically I’ll be replacing anonymous functions like so:
You will notice that A, B, and C print in a different and random order each time you call printAll!
This is because these functions are asynchronous. Each function gets executed in order, but each one is independent with it’s own setTimeout. They won’t wait for the last function to finish before they start.
This is super annoying, so let’s fix it with a callback.
Callbacks
A callback is a function that is passed to another function. When the first function is done, it will run the second function.
Well, the code is a lot uglier now, but at least it works! Each time you call printAll, you get the same result.
The problem with callbacks is it creates something called “Callback Hell.” Basically, you start nesting functions within functions within functions, and it starts to get really hard to read the code.
Promises
Promises try to fix this nesting problem. Let’s change our function to use Promises
You can see that it still looks pretty similar. You wrap the whole function in a Promise, and instead of calling the callback, you call resolve (or reject if there is an error). The function returns this Promise object.
Again, let’s try to print the letters A, B, C in that order:
This is called a Promise Chain. You can see that the code returns the result of the function (which will be a Promise), and this gets sent to the next function in the chain.
The code is no longer nested but it still looks messy!
By using features of arrow functions, we can remove the “wrapper” function. The code becomes cleaner, but still has a lot of unnecessary parenthesis:
Await is basically syntactic sugar for Promises. It makes your asynchronous code look more like synchronous/procedural code, which is easier for humans to understand.
The printString function doesn’t change at all from the promise version.
Again, let’s try to print the letters A, B, C in that order:
You might notice that we use the “async” keyword for the wrapper function printAll. This let’s JavaScript know that we are using async/await syntax, and is necessary if you want to use Await. This means you can’t use Await at the global level; it always needs a wrapper function. Most JavaScript code runs inside a function, so this isn’t a big deal.
BUT WAIT THERE'S MORE
The printString function doesn’t return anything and is independent, all we cared about was the order. But what if you wanted to take the output of the first function, do something with it in the second function, and then pass it to the third function?
Instead of printing the string each time, let’s make a function that will concatenate the string and pass it on.
functionaddAll(){
addString('', 'A', result => {
addString(result, 'B', result => {
addString(result, 'C', result => {
console.log(result) // Prints out " A B C"
})
})
})
}
functionaddAll(){
addString('', 'A')
.then(result => {
return addString(result, 'B')
})
.then(result => {
return addString(result, 'C')
})
.then(result => {
console.log(result) // Prints out " A B C"
})
}
addAll()
Using arrow functions means we can make the code a little nicer:
functionaddAll(){
addString('', 'A')
.then(result => addString(result, 'B'))
.then(result => addString(result, 'C'))
.then(result => {
console.log(result) // Prints out " A B C"
})
}
addAll()
This is definitely more readable, especially if you add more to the chain, but still a mess of parenthesis.
Await
The function stays the same as the Promise version.
And in order to call it:
asyncfunctionaddAll(){
let toPrint = awaitaddString('', 'A')
toPrint = awaitaddString(toPrint, 'B')
toPrint = awaitaddString(toPrint, 'C')
console.log(toPrint) // Prints out " A B C"
}