WTF is Asynchronous JavaScript: async/await vs .then()
Learn the differences between .then() and async/await in JavaScript for handling asynchronous operations efficiently.
Introduction
Hey there! Welcome back to the new blog post in the "WTF is JavaScript" series.
In today's article, we will understand the asynchronous capabilities of JavaScript. Also, we will learn about how to use .then() and async/await in your code.
In this series, we are exploring key modern JavaScript concepts, one topic at a time. Checkout the series to find more such insightful content and improve your JavaScript skills.
Is JavaScript Synchronous or Asynchronous?
JavaScript is both synchronous and asynchronous, depending on how it's used. This duality arises from JavaScript's single-threaded execution model and its need to handle tasks that may take some time to complete, such as fetching data from a server or reading a file.
Synchronous JavaScript:
JavaScript is inherently synchronous by nature, which means it executes code line by line, one after the other.
Synchronous operations block the execution of subsequent code until the current operation is completed. For example, if you have a long-running calculation, it will prevent other tasks from running until it's finished.
Asynchronous JavaScript:
JavaScript also supports asynchronous operations to prevent blocking. This is essential for tasks like making HTTP requests, reading files, or waiting for user input.
Asynchronous operations allow JavaScript to start a task, and then move on to other tasks without waiting for the first task to complete. When the asynchronous task finishes, it triggers a callback function or resolves a promise.
It means that JavaScript is synchronous by default to ensure predictable execution but provides mechanisms like callbacks, promises, and async/await to handle asynchronous operations efficiently and maintain responsiveness in web applications.
Understand async programming with .then() and async/await
This is where .then()
and async/await
come into play. They provide structured ways to work with asynchronous operations, allowing developers to write code that's easier to understand, manage, and maintain while ensuring responsiveness.
The .then() Approach: Old But Gold
Let's start with the .then()
approach, which is based on Promises—a JavaScript feature introduced to handle asynchronous operations.
Suppose you want to fetch and display a list of user names from an API. Here's how you might use .then()
:
fetch('https://jsonplaceholder.typicode.com/users')
.then(response => response.json())
.then(data => {
const usernames = data.map(user => user.username);
console.log(usernames);
})
.catch(error => console.error(error));
Output:
["Bret", "Antonette", "Samantha", "Karianne", "Kamren", "Leopoldo_Corkery", "Elwyn.Skiles", "Maxime_Nienow", "Delphine", "Moriah.Stanton"]
Here's how it works:
We fetch data from the API.
We use
.then()
to handle the response.We convert the response to JSON.
We log the data or handle errors with
.catch()
.
Problems with .then() - Callback Hell
While .then()
is useful, but it can become problematic when you have multiple asynchronous tasks or deeply nested operations. This situation is often referred to as "callback hell" because the code becomes difficult to read and maintain.
example:
Imagine you need to fetch user data and their posts, and then log both. Here's how it might look in callback hell:
fetch('https://jsonplaceholder.typicode.com/users/1')
.then(userResponse => userResponse.json())
.then(userData => {
fetch(`https://jsonplaceholder.typicode.com/posts?userId=${userData.id}`)
.then(postsResponse => postsResponse.json())
.then(postsData => {
console.log('User Data:', userData);
console.log('User Posts:', postsData);
})
.catch(error => console.error(error));
})
.catch(error => console.error(error));
As you can see, the code becomes deeply nested and challenging to follow.
Introduction to async/await: simpler way
To address the issues of callback hell and simplify asynchronous code, JavaScript introduced async/await
. It allows developers to write asynchronous code in a more linear and structured way.
How async/await Helps
Here are a few key benefits of async/await
:
Readability: Asynchronous code can be written more like synchronous code, making it easier for developers to follow the flow of the program.
Error Handling: Errors are easier to handle with
try...catch
blocks, leading to more robust and maintainable code.Structured: Code becomes more structured and organized, reducing the need for deeply nested callbacks.
Now, let's see how async/await
works with an example.
Code Example with Output
async function fetchUsernames() {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/users');
const data = await response.json();
const usernames = data.map(user => user.username);
console.log(usernames);
} catch (error) {
console.error(error);
}
}
fetchUsernames();
Here's how async/await works:
We declare an
async
functionfetchUsernames
.Inside the function, we use
await
to pause execution until the promise is resolved.We handle success and errors using a simple
try...catch
block.
Output:
["Bret", "Antonette", "Samantha", "Karianne", "Kamren", "Leopoldo_Corkery", "Elwyn.Skiles", "Maxime_Nienow", "Delphine", "Moriah.Stanton"]
Why async/await shines?
As you can see, the code is more structured and easier to understand compared to the .then()
approach. It reads like a series of steps, from fetching the data to logging the usernames.
It's clear, organized, and easier to handle errors. No callback hell, just smooth, readable code.
Async/await vs .then(): what to choose
When to Choose .then()
:
Familiarity: If you or your team are already comfortable with using
.then()
, and the codebase predominantly uses it, sticking with it may be the simplest choice.Simple Sequences: For straightforward, single asynchronous operations, such as fetching data from an API or making a single HTTP request,
.then()
can provide a clear and concise way to handle the task.Chaining:
.then()
allows for easy chaining of multiple asynchronous operations in a linear manner, which can be effective for simpler scenarios.
When to Choose async/await
:
Readability:
async/await
code reads more like synchronous code, making it easier to understand and maintain, especially for complex asynchronous logic.Error Handling:
async/await
simplifies error handling withtry...catch
blocks, leading to cleaner and more robust code.Structured Code: If you need to work with multiple asynchronous operations,
async/await
provides a structured way to handle them, reducing the risk of callback hell.Variable Scope: Variables declared with
await
are limited to the scope of theasync
function, preventing accidental variable leakage.Debugging: Debugging is typically easier with
async/await
because the code execution follows a more predictable, linear flow.
Practical Tips for Easier Coding
For .then(): Give your functions clear names, so it's like reading a recipe book.
For async/await: Use arrow functions to make your code shorter and sweeter.
Conclusion
In this introductory article, we explored JavaScript's synchronous and asynchronous nature.
We saw how .then()
and async/await
are essential tools for managing asynchronous operations. .then()
is useful but can lead to callback hell in complex scenarios.
In contrast, async/await
simplifies asynchronous code, making it more readable and structured.
Thank you for reading!
That's it for this blog post, and stay tuned for upcoming blogs in the WTF is Javascript series. Thank you for reading this article!
If you enjoyed this article, be sure to subscribe to my newsletter to stay up-to-date with my content.
And if you need any help or have any questions, don't hesitate to comment below! I'll be happy to help you out.