This article was written by Adeola Ajiboso
Asynchronous simply refers to making many events occur simultaneously in any order without waiting for one another, and this definition applies to asynchronous Javascript.
In this article, I'll go through the concept of asynchronous JavaScript, Callback, Promises, Async/Await, Callback queue, Event Loop, and Web browser features (Web API).
Javascript is a single-threaded language that can only run one process at a time. JavaScript uses an event loop to carry out the operation, for now, consider the event loop as a queue where all Javascript processes are maintained and executed one at a time.
You should note Javascript is actually a Synchronous language. JavaScript executes lines of code sequentially, not concurrently, because it is by default, synchronous and single-threaded.
For instance, the code below will run and be executed line by line.
let username = "John";
let points = 5000;
let message = `${username} has earned ${points} points`;
console.log(message);
//Output
John has earned 5000 points
The code above is executed synchronously, each line is completed before moving on to the next line.
Let's consider a scenario where you need to retrieve a large amount of data from an API and display it. By default, in JavaScript, the execution is blocked, and all further instructions are halted until the retrieval request is completed. This is where asynchronous programming becomes crucial. GIF
Let’s dive in.
What is Asynchronous JavaScript
Asynchronous JavaScript refers to the programming paradigm in which code execution does not follow the usual sequential flow from top to bottom. Instead, asynchronous code allows certain operations to be initiated and completed separately without blocking the execution of other code.
The concept of asynchronous JavaScript enables you to break down big complex projects into smaller tasks.
What is involved in Asynchronous JavaScript?
- Web Browser features (Web API).
- Callback queue.
- Event loop.
- Callback.
- Async/Await.
- Promise.
Web Browser Features (Web API)
Browser APIs, also known as web APIs, are pre-existing interfaces embedded within web browsers. These APIs offer built-in functionalities that can be leveraged within web applications.
Web APIs allow developers to access and interact with these additional functionalities using JavaScript. By utilizing web APIs, developers can seamlessly incorporate specific features into their codebase while minimizing code complexity. Examples of such features include making network requests and efficiently managing client-side storage.
Some web browser features include:
setTimeout()
The setTimeout()
method allows you to run a block of code after a certain time delay. It is designed to execute the code once.
The syntax of setTimeout()
is:
setTimeout(function, milliseconds);
Example:
function username() {
console.log("John")
}
setTimeout(username, 5000)
console.log("Doe")
Output:
Doe
John
setInterval()
The setInterval()
method repeats a block of code at every given timing event.
The syntax of setInterval()
is :
setInterval(function, milliseconds);
Example:
// program to display a text using setInterval method
function orderItem() {
console.log('I want a bag');
}
setInterval(orderItem, 1000);
Output:
I want a bag
I want a bag
I want a bag
I want a bag
...
In the above program, the
orderItem()
function is invoked by the setInterval()
method at an interval of 1000 milliseconds. Consequently, the program outputs the message "I want a bag" every 1 second.
clearTimeout()
clearTimeout()
method cancels a timeout previously established by callingsetTimeout()
.
The syntax of clearTimeout()
is:
clearTimeout(timeoutId)
Example:
// Set a timeout that logs a message after 3 seconds
const timeoutId = setTimeout(() => {
console.log("Timeout executed!");
}, 3000);
// Clear the timeout before it executes
clearTimeout(timeoutId);
Output:
If you run the code snippet provided, there will be no output. The
clearTimeout()
function cancels the execution of the timeout set by setTimeout()
preventing the callback function from being invoked. As a result, the message “Timeout executed!" will not be logged to the console.
clearInterval()
clearInterval()
is used If you want to stop the function call.
The syntax of clearInterval()
is:
clearInterval(intervalId);
Example:
// Define a variable to store the interval ID
let intervalId;
// Function to be executed repeatedly
function showMessage() {
console.log("Hello!");
}
// Start the interval
intervalId = setInterval(showMessage, 1000);
// After 5 seconds, stop the interval
setTimeout(() => {
clearInterval(intervalId);
console.log("Interval stopped");
}, 5000);
Output:
Hello!
Hello!
Hello!
Hello!
Interval stopped
In the above program, you first declare a variable
intervalId
to store the ID returned by setInterval
function. The showMessage
function is defined, which will be executed repeatedly every 1000 milliseconds (1 second) using setInterval
.After 5 seconds (5000 milliseconds),
clearInterval
is called with the intervalId
as the argument to stop the interval. This will stop the execution of the showMessage
function. Finally, a message is logged to the console indicating that the interval has been stopped.
fetch()
The fetch() method in JavaScript is used to request data from a server.
They are not part of the JavaScript language, but they are built on top of the core JavaScript language, providing you with extra superpowers to use in your JavaScript code.
Read more about Web APIs 👉 here 👈.
Callback queue
The callback queue is where the callback function is pushed and awaits execution. The callback queue follows the First-In-First-Out(FIFO) principle.
Now let’s go through this example:
function username() {
console.log("John")
}
setTimeout(username, 5000)
console.log("Doe")
//output
Doe
John
This block of code demonstrates the usage of the
setTimeout()
function to delay the execution of a callback function.
Here's how it works:
- The
username
function is defined and stored in the Global memory, which logs the string "John" to the console. - The
setTimeout()
function is called with two arguments. The first argument is theusername
function, which specifies the callback function to be executed, and the second argument is the delay in milliseconds, which in this case is 5000 milliseconds (or 5 seconds). - After calling
setTimeout()
, the code continues to the next line and logs the string "Doe" to the console. - The string "Doe" is logged to the console first.
- After the delay of 5000 milliseconds, the
username
function is executed and logs the string "John" to the console.
Event Loop
The event loop continuously checks if the call stack is empty. If the call stack is empty, it means that there are no functions currently being executed. In that case, the loop takes the functions waiting in the callback queue and pushes them onto the call stack for execution. The event loop acts like a gatekeeper for the callback queue.
The event loop manages code execution to avoid blocking, enabling the program to proceed with its operation even during the completion of asynchronous tasks.
What is Callback?
When you pass a function as an argument to another function, and that function is invoked or executed within the outer function, it is commonly referred to as a callback function.
Now let’s go through this example:
function placeOrder() {
setTimeout(() => {
return (Math.random() * 10) <= 5 ? 'Bag' : 'Shoe';
}, 2000);
}
let order = placeOrder();
console.log('Order is for: ' + order);
//Output
// Order is for: undefined
Here in the
placeOrder
function, the setTimeout()
will run after 2 seconds, and by that time the console.log
statement has already been executed, the printed value of order
is undefined.
Now, you can resolve this issue by logging your message to the console only after the data has returned from placeOrder
. This can be done by passing a callback function to placeOrder
, which will be invoked inside the placeOrder
function.
function placeOrder(callback) {
setTimeout(() => {
const order = (Math.random() * 10) <= 5 ? 'Bag' : 'Shoe';
callback(order);
}, 2000);
}
placeOrder((order) => {
console.log('Order is for: ' + order);
});
//Output
// Order is for: Bag
After two seconds, the callback function will be called, and the console statement will get executed with the correct order value.
The output of placeOrder function may differ in your case as you are using Math.random()
to decide order value.
Promise
Promise is an object in JavaScript that represents the eventual completion (or failure) of an asynchronous operation and its resulting value.
Promises simplify handling asynchronous code, making it easier to write and maintain asynchronous operations without resorting to callback functions or nested callbacks (known as callback hell).
Callback hell is a term used to describe a situation where the code becomes hard to read and understand due to a large number of nested callback functions.
A promise can be in one of three states:
- Pending: The initial state when a promise is created, and the asynchronous operation is still ongoing.
- Fulfilled: The state when the asynchronous operation is successfully completed, and the promise has a resolved value.
- Rejected: The state when the asynchronous operation encounters an error or fails, and the promise has a reason for rejection.
There are three main aspects of promises.
- Creating a promise
- Handling a promise
- Chaining of promise
Creating Promises
To create a promise, you use the Promise constructor, which accepts a function (commonly referred to as the executor) as an argument. The executor function is provided with two parameters: resolve and reject. Within the executor function, you execute your asynchronous task and invoke either resolve or reject based on the result.
Here's an example of creating a promise :
const fetchData = new Promise((resolve, reject) => {
// Simulating asynchronous task (e.g., making an API call)
setTimeout(() => {
const data = { id: 1, name: "John Doe" };
// Fulfilled the promise with the fetched data
resolve(data);
reject(new Error("Failed to fetch data"));
// Alternatively, reject the promise with an error
}, 2000);
});
Handling a Promise
After creating a promise, you can assign callbacks to handle the result using the then()
and catch()
methods.
The then()
method takes a callback function as an argument and manages the fulfilled promise. It receives the resolved value or result of the promise as an argument.
The catch()
method is used to handle the rejection of a promise and it is called when the promise is rejected, allowing you to handle any errors that occurred during the asynchronous operation.
Now let's go through this example:
fetchData
.then((data) => {
// Handle the fulfilled promise (access the resolved value)
console.log("Fetched data:", data);
})
.catch((error) => {
// Handle the rejected promise (access the error)
console.log("Error:", error);
});
Chaining Promises
Using the then()
method, you can chain promises together to create a sequence of asynchronous operations. Each then()
callback produces a new promise, enabling you to handle the outcome of the previous operation and proceed with the next one. Chaining promises allows you to establish a more sequential and readable flow of asynchronous operations.
fetchData
.then((data) => {
// Handle the first fulfilled promise
console.log("Fetched data:", data);
return someOtherAsyncTask();
// Return a new promise
})
.then((result) => {
// Handle the result of the second fulfilled promise
console.log("Second async task result:", result);
})
.catch((error) => {
// Handle any errors in the chain
console.log("Error:", error);
});
Async/Await
Async/Await is a relatively recent addition to JavaScript, introduced in ES8, that provides a means to write asynchronous code in a synchronous manner.
If you use the Async
keyword before the function definition, you can then use await within the function. Await
gives you the power to pause the function in a non-blocking way until the promise is resolved.
Now let's go through this example:
async function fetchData() {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/posts');
const data = await response.json();
console.log('Fetched data:', data);
} catch (error) {
console.log('Error fetching data:', error);
}
}
fetchData();
Here’s the explanation to the code snippet:
- An async function called
fetchUserData()
is declared. Inside this function, an asynchronous HTTP request is made to the API endpoint using thefetch()
function. Thefetch()
function returns a promise that resolves to a response object. - The execution is paused using the
await
keyword, allowing the code to wait for the promise returned byfetch()
to resolve. The resolved response object is stored in theresponse
variable. - Another
await
keyword is used to pause the execution and wait for the promise returned by theresponse.json()
method. This method parses the response body as JSON. The parsed JSON data is then stored in thedata
variable. - Finally, the fetched user data is logged to the console. If any errors occur during the execution of the async function, they are caught in the
catch
block, enabling the handling and logging of the errors.
Conclusion
In this article, you’ve learned what asynchronous JavaScript is and how to write asynchronous JavaScript using promises and async/await. You’ve also seen how to send requests using the fetch API and async/await and how to return a response to asynchronous calls.
In JavaScript, synchronous instructions always take precedence over asynchronous instructions. For instance, if you have a scenario with numerous console.log statements followed by a setTimeout() function with a duration of 0 milliseconds, all the console.log statements will be executed first before the setTimeout().
I hope this article was informative? You can give it a like or comment on what you think.
Adeola Ajiboso is available on Twitter, LinkedIn, or GitHub. Keep an eye out for my upcoming blog post, in which I'll go over another important area of web development. As a developer, I'm glad to provide additional information. Until then, happy coding, and take care!
If you find this post exciting, find more exciting posts on Learnhub Blog; we write everything tech from Cloud computing to Frontend Dev, Cybersecurity, AI, and Blockchain.
Resources
Here are some blogs/posts I read as a reference. You should check them out:
- Asynchronous JavaScript (JS) Demystified By Thanh Truong's
- JavaScript Async/Await Tutorial – Learn Callbacks, Promises, and Async/Await in JS by Making Ice Cream 🍧🍨🍦 By Joy Shaheb
Top comments (3)
I Bet You've Been Doing Async/Await Wrong
José Pablo Ramírez Vargas ・ Dec 21 '22
Great explanation thanks a bunch 😁
Most welcome.