Understanding Asynchronous JavaScript: A Beginner's Guide


What is an Asynchronous Function?
An Asynchronous function in programming is a function that can operate independently of the main program flow, meaning it doesn’t block the execution of the rest of the code.
Benefits of Asynchronous Functions:
Improved Performance: By avoiding blocking, Asynchronous functions allow programs to respond to user input or perform other tasks while waiting for long-running operations.
Enhanced Responsiveness: User interfaces remain responsive even when complex operations are in progress.
Use Cases:
Network Requests: Fetching data from servers, API calls.
File Input/Output: Reading or writing files.
Timers and Events: Handling events or waiting for specific time intervals.
User interface updates: Updating the display without blocking user interactions.
Here are 3 Main ways to achieve Asynchronous Behavior in JavaScript:
Before we dive into Asynchronous behavior, I'll use a method called setTimeout() in this guide. We'll quickly go over what the setTimeout() method does.
setTimeout() - It is a built-in function that allows you to execute a function after a specified delay.
Syntax: setTimeout(function, delay, …args)
function: The function to execute after the delay.
delay: The delay in milliseconds.
…args: optional additional arguments to pass to the function.
In the example above, the program prints "Hello World" after a 6-second delay.
Note: In the setTimeout function above, we used an arrow function, shown as () => {}
.
Now we are ready to explore the concepts.
Callback Function:
A callback function is simply a function that is passed as an argument to another function and is executed after some kind of event or task. It allows you to run code after something else has completed, enabling asynchronous behavior and more flexible code structures.
Why use Callbacks?
1. Handle asynchronous tasks (like waiting for an API request).
2. Reuse code by passing different behaviors into the same function.
3. Avoid blocking the main thread (especially in the browser).
Example 1:
In this example 1:
greet() is a function that takes a name and a callback.
After greeting the person, it calls callback(), which is askQuestion().
Example 2:
In this example 2 , the setTimeout() function takes a callback function and executes it after a delay.
Example 3:
In this example 3, sumOfTwoNumber()
is a function that takes three parameters: num1
, num2
, and a callback function. When sumOfTwoNumber()
is called, it adds the two numbers and stores the result in the sum
variable. Then, it prints a message and calls the callback function, display()
, passing the sum
variable to it. The display()
function then prints the result of the sum of the two numbers.
Summary:
A callback is a function passed as an argument.
It’s called after a task is completed.
It’s essential for handling Async tasks.
Modern Alternatives like Promises and async/await are often preferred for complex cases.
Promises:
A Promise is an object that represents the eventual completion or failure of an asynchronous operation. It’s like a placeholder for a value that isn’t available yet but will be at some point.
A Promise can be:
Pending: The operation is still ongoing.
Fulfilled: The operation completed successfully, and the promise has a value.
Rejected: The operation failed, and the promise has a reason (usually an error).
Promise Syntax:
The Promise Object supports two Properties: state and result.
When a promise object is “pending” (working), the result is undefined.
When a promise object is “fulfilled”, the result is a value.
When a promise object is “rejected”, the result is an error object.
Example 1:
new Promise()
: Creates a new Promise object, which represents a task that might complete in the future.
It takes a function with two arguments:
myResolve
: Call this when the task succeeds.myReject
: Call this when the task fails.Inside the promise:
We define
let x = 0;
.If
x == 0
, we callmyResolve("OK");
, signaling success with the value"OK"
.Otherwise, we call
myReject("ERROR");
, signaling failure with the value"ERROR"
.
myPromise.then(successCallback, failureCallback)
If the Promise is resolved (success), it calls the
successCallback
with the resolved value.If the Promise is rejected (failure), it calls the
failureCallback
with the rejected value.
Example 2: How to handle try and catch using promises
Resolved state:
This line creates a new Promise object and assigns it to
myPromise
.The Promise constructor takes a function as an argument, which itself takes two parameters:
resolve
andreject
.resolve
is called when the asynchronous operation is successful.reject
is called if there’s an error.
This line schedules an asynchronous action to run after 3 seconds.
When it runs, it prints "Code has error" to the console.
Note: despite the message "Code has error", this doesn't actually trigger any rejection in the Promise. It's just a console log.
This line immediately resolves the Promise with the value
223
.Note that this happens before the
setTimeout
runs, becauseresolve(223)
is synchronous, whilesetTimeout
is asynchronous.The
.then()
method attaches a handler to the Promise that executes once the Promise is resolved.When
resolve(223)
is called,.then()
gets triggered with the value223
Failed state
✅ The Promise is immediately rejected, triggering
.catch()
, which logs"Received an error"
.
⏳ Meanwhile, thesetTimeout
logs"Code has error"
after 3 seconds, but it does not affect the Promise’s rejected state.
🔍 What is .then()
?
.then()
is a method on a Promise that allows you to:
✅ Handle the result of a successfully resolved Promise,
✅ or chain multiple asynchronous operations together.
It takes two optional arguments:
onFulfilled — a function to run when the Promise is resolved.
onRejected — a function to run when the Promise is rejected (although usually
.catch()
is preferred for error handling).
🔍 What is .catch()
?
.catch()
is a method on a Promise that lets you handle any errors (rejections) that occur during the asynchronous operation. It’s often paired with .then()
to handle both success and failure in a clean way.
What is Promise Chaining?
Promise chaining means linking multiple asynchronous operations together so that each one starts after the previous one finishes.
In JavaScript, since Promises return another Promise by default, you can chain them together using .then()
.
🤯 Key Benefits:
✅ Each step starts after the previous one completes.
✅ Errors propagate to the .catch()
at the end, simplifying error handling.
✅ Code is flat and easy to read—no deeply nested callbacks.
🚨 Gotchas:
If you forget to
return
a Promise inside.then()
, the next step won’t wait—it’ll run immediately..catch()
can be anywhere in the chain, but putting it at the end is most common.
Example:
Promise 1:
A new promise is created, but resolve is called immediately, before the
setTimeout
actually completes.The
setTimeout
runs after 2 seconds.However, because
resolve()
is called right away,.then()
on this promise runs immediately, without waiting for the timeout.
Promise 2:
When
promise1
is resolved (which happens immediately),.then()
triggers and runs this code.Inside this
.then()
, a new promise is created.Again,
resolve()
is called immediately after defining thesetTimeout
, so technically.then()
could be called even before the timeout logs.However, because of the way the promise is returned, the timeout is inside the promise, so it ensures that the chain waits until this promise is resolved.
Promise 3:
When
promise2
resolves,.then()
triggers:Inside, another promise is created
Again,
resolve()
is called immediately.
Since none of the promises are actually returning a value (they just call
resolve()
without passing anything), thisconsole.log(value)
will printundefined
.If you want to print something meaningful here, you'd need to
resolve("some message")
inside each promise.
🕒 Timing Breakdown
0 seconds:
promise1
resolves, startingpromise2
.2 seconds: logs "Promise-1 is completed..."
2 seconds:
promise2
starts its timer.5 seconds: logs "Promise-2 is completed..."
5 seconds:
promise3
starts its timer.9 seconds: logs "Promise-3 is completed..."
9 seconds: logs
undefined
(thevalue
in the final.then()
).
Async/Await:
Async and await are modern ways to handle asynchronous code in JavaScript, making it look and behave more like synchronous code.
Why use them?
They:
Make asynchronous code easier to read and write.
Help avoid callback hell or complex
.then()
chaining.Improve error handling.
🔧 Basic Syntax:
async
Put
async
before a function to make it always return a Promise.Inside that function, you can use
await
to pause execution until the promise is settled (either resolved or rejected).
await
Used inside an
async
function to wait for a promise to complete.Makes JavaScript wait for the promise’s result before continuing.
Example 1:
fetchData
is a function that returns a Promise.Inside the Promise, it uses
setTimeout
to simulate a delay of 2 seconds (2000 milliseconds) — like you might have when fetching data from an API.After 2 seconds, it resolves the Promise with the value
"Data fetched!"
getData
is an async function, which allows you to use theawait
keyword.It starts by logging
"Fetching data..."
.Then it calls
await fetchData()
, which pauses execution ofgetData
untilfetchData
resolves.When
fetchData
resolves,result
gets the value"Data fetched!"
.Finally, it logs the result.
Example 2:
fetchData
Function:
This function simulates an asynchronous operation (like fetching data from a server).
It returns a Promise.
Inside the promise:
setTimeout
waits for 2 seconds.Then it rejects the promise with the error message:
"Data could not be fetched"
.
No success (resolve) is called here, so this promise always fails after 2 seconds.
getData
Function:
This is an async function, which means it always returns a Promise.
It logs:
"Fetching data..."
immediately.Then it uses
await
to pause execution untilfetchData()
settles.- Since
fetchData()
always rejects, theawait
expression throws an error.
- Since
The error is caught by the
catch
block.console.error("Error: ", error);
logs:Error: Data could not be fetched
.
getData(): This calls
getData()
, which kicks off the process.
What is fetch?
The Fetch API is a modern way to make network requests (such as retrieving data from a server) in JavaScript. It’s built-in and returns a Promise.
It’s widely used to get (or send) data asynchronously, replacing older APIs like XMLHttpRequest
.
Basic Syntax:
url
: the endpoint you want to request (e.g.,"
https://api.example.com/data
"
).fetch
returns a Promise that resolves to a Response object.
(response) In JavaScript:
When you use the fetch()
API to make a network request, it returns a Promise that resolves to a Response object.
🔎 The Response
object represents the entire HTTP response, including:
✅ Status code (e.g., 200, 404, 500)
✅ Headers (like Content-Type, Authorization)
✅ The body (the actual data) — but as a readable stream.
response.json() In JavaScript:
The body of the Response object is a stream — so it needs to be read.
That’s where response.json()
comes in!
✅ response.json()
reads the response body and parses it as JSON, returning a Promise that resolves to a JavaScript object.
Example:
How it works:
✅ response
is the raw HTTP response object.
✅ response.json()
reads the body stream of that response and parses it into a usable JavaScript object.
What is a POST request?
A POST request is used to send data to a server, often to create or update resources.
Example 1:
Example 2:
🌟 Conclusion (Short)
In JavaScript, asynchronous tasks can be handled using callbacks, Promises, and async/await. Callbacks can get messy, but Promises and async/await make code cleaner and easier to read. The Fetch API is a modern way to make HTTP requests, returning Promises that let you handle data with .then()
or await
. Always remember to handle errors with .catch()
or try/catch
for reliable, maintainable code.
Subscribe to my newsletter
Read articles from Rishabh Kumar directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
