Article originally published on Tinloof
JavaScript code is executed synchronously. In other words, from top to bottom and one line at a time.
function getText() {
return "Hi π, I'm Tinloof";
}
let text = getText();
console.log(text);
// Output:
// Hi π, I'm Tinloof
First, the code will execute the function and know what to return when getText()
is called.
Then it assigns the getText()
function to the variable text
.
Finally, it logs the variable text to the console, and the output is "Hi π , I'm Tinloof".
So far, this works great and we're facing no obstacle.
Now, imagine we have to make a network request to get the text "Hi π , I'm Tinloof" and the user doesn't have a fast Internet connection.
// Assume getTextFromServer is making a network request to get data
let text = getTextFromServer();
// π° Wait until we receive the text from the server
// π¦ Meanwhile the page is frozen and the user can't interact with it
console.log(text);
// Output:
// Hi π, I'm Tinloof
The code works, but while we wait for the text from the server, our page freezes.
One appraoch to solve this problem is called "callbacks".
Callback functions
getTextFromServer((error, text) => {
if (error) {
console.log("Error getting the text:", error);
} else {
console.log(text);
}
});
// Output (if we successfully get the text from the server)
// Hi π , I'm Tinloof
// Output (if we are not successfully getting the text from the server)
// Error getting the text: some error from the server.
Instead of waiting for getTextFromServer()
to finish, we let it run in the background and pass to it a function, called "callback function" or "callback", to handle the result of the call once it's done.
To handle the scenario where the request fails or the one where it succeeds, our callback function takes 2 parameters:
- An error, which is empty if the request is successful
- A result (in our case it's the text "Hi π , I'm Tinloof")
Notice: If you're not familiar with arrow functions, here's a link to the MDN documentation.
We just wrote our first asynchronous code!
Guess what? We just learned one approach of writing asynchronous code in JavaScript.
In the example above, while our code is looking to get the text from the server, the rest of our code would still run.
Another approach to asynchronous code is called Promises.
Promise
let promise = getTextFromServer();
promise
.then((text) => {
console.log(text);
})
.catch((error) => {
console.log("Error getting the text:", error);
});
// Output (if we successfully get the text from the server)
// Hi π , I'm Tinloof
// Output (if we are not successfully getting the text from the server)
// Error getting the text: some error from the server.
Instead of accepting a callback, getTextFromServer()
returns a Promise object.
A Promise is an object that gives us the result of the success of an asynchronous operation or the result of its failure (it either resolves or rejects).
It does that by providing a then()
function to handle success and catch()
to handle errors.
JavaScript has a syntatic sugar (jargon for "more beautiful syntax") for Promises, let's check it out.
async/await
try {
let text = await getTextFromServer();
console.log(text);
} catch (error) {
console.log("Error getting the text:", error);
}
// Output (if we successfully get the text from the server)
// Hi π , I'm Tinloof
// Output (if we are not successfully getting the text from the server)
// Error getting the text: some error from the server.
Instead of using the Promise syntax, which can be confusing at times, we simply await getTextFromServer()
using the await
keyword.
To handle the error and success scenarios, we surround our code in a try/catch
block.
If the request is successful, the try
block will be executetd to its end and the text will be printed.
If the request fails, we'll jump directly from the await line to the catch
block and output the error.
Using "await" in a function
If we want to use the await
syntax in a function, the function has to be declared with the async
keyword.
async function getText() {
try {
let text = await getTextFromServer();
console.log(text);
} catch (error) {
console.log("Error getting the text:", error);
}
}
console.log(getText);
// Output (if we successfully get the text from the server)
// Hi π , I'm Tinloof
// Output (if we are not successfully getting the text from the server)
// Error getting the text: some error from the server.
Conclusion
We now know what Asynchronous JavaScript is, and learned how to write it with 3 approaches:
- Callback functions
- Promises
-
async...await
(which is just a prettier syntax of Promises)
Believe it or not, if you made it so far while understanding everything, you can build most features that require asynchronous code.
Though, we strongly advise you to dig deeper in the topic. Here are a few resources:
Rethinking Asynchronous JavaScript by Kyle Simpson on Frontend Masters. You can also read the book YDKJS for free here
Top comments (0)