Hello there, coding fanatic! đď¸ If youâre reading this, itâs likely that youâve heard the words âsynchronousâ and âasynchronousâ used in JavaScript.
But what exactly do they mean? How do they shape the way JavaScript behaves? How do they influence the way we write our code? And how do we give preference on which one we should use in our code? This post aims to answer all these questions and more.
We will together learn synchronous and asynchronous programming in JavaScript, understand what Callbacks and Promises are, and explore how they help us control JavaScriptâs asynchronous behavior.
Whether youâre a beginner or someone brushing up their JavaScript skills, I hope this article will help you to get comprehensive knowledge on both synchronous and asynchronous programming! So letâs get started.
Synchronous JavaScript
JavaScript is a single-threaded language by definition. This means that it can execute only one statement at a time in the order in which they appear. This behavior is simply known as synchronous programming.
Hereâs a basic example:
console.log('First');
console.log('Second');
console.log('Third');
In this code, âFirstâ is always logged before âSecondâ, and âSecondâ is always logged before âThirdâ. That is an example of synchronous JavaScript!
Limitations of Synchronous Programming
While synchronous programming is easier to understand and use, it has limitations. Synchronous JavaScript has a significant drawback â itâs blocking.
What does that mean? đ¤ If one action, like fetching data from an API, querying a database, or reading a file, takes a long time to finish, everything that comes after it has to wait for it to finish.
Imagine standing in line and waiting for your turn. Thatâs how JavaScript code works. This can significantly slow down your code execution and lead to a poor user experience.
Furthermore, network requests, user interactions, and timers are inherently asynchronous on the modern web. Sticking to synchronous programming may prevent you from fully using the event-driven nature of JavaScript.
Asynchronous JavaScript
Asynchronous JavaScript can save our time dramatically in this case! Asynchronous JavaScript allows us to start a time-consuming operation, go on to the next task, and then return to the original operation when it is complete.
Why and When You Need Asynchronous JavaScript?
Asynchronous programming in JavaScript is essential for several reasons. First, JavaScript is inherently asynchronous due to its event-driven nature. This means that events such as clicks, mouse movements, and keyboard presses are all handled asynchronously.
Additionally, operations like network requests, reading or writing to a file system, and heavy computations that can take a considerable amount of time are also handled asynchronously.
In these cases, using asynchronous programming techniques allows your application to continue processing other tasks while waiting for these operations to finish.
Here are some scenarios where asynchronous programming in JavaScript would be beneficial:
1 â Making API calls: When fetching data from an API, you donât want to block the rest of your code while waiting to return the data. Asynchronous programming allows you to initiate the API call and then execute other code. Once the data is returned from the API, you can then use it in your application.
Just like when you open a social media post, you wouldnât want the user to wait until all comments are loaded before they can view the post. An asynchronous approach allows the post to display immediately, with comments loading in real-time as theyâre fetched.
2 â Reading/Writing to a Database or File System: Similar to API calls, operations involving a database or file system can take a while to complete. Using asynchronous programming allows your application to continue running other tasks while waiting for these operations to finish.
Consider an e-commerce siteâs product page. Youâd want the main product details to load instantly, while recommendations based on user behaviour or related products can load asynchronously, offering a seamless shopping experience.
3 â Image Processing or Heavy Computations: If youâre performing heavy computations or processing large images, asynchronous programming can prevent these operations from blocking your JavaScript application.
4 â Event-driven Programming: Many events in JavaScript, like clicks, mouse movements, keyboard presses, etc., are handled asynchronously. This way, your application remains responsive and ready to handle other user actions even if one event handler is still processing.
Callbacks
In JavaScript, a callback function is a function that is passed as an argument to another function and is executed after some operation has been completed. Hereâs a simple example:
function fetchData(callback) {
// Simulate delay using setTimeout
setTimeout(function() {
console.log('Data fetched');
callback();
}, 2000);
}
function processData() {
console.log('Processing data');
}
// Data fetched (after 2 seconds) -> Processing data
fetchData(processData);
In this snippet, fetchData
takes a long time to run (2 seconds). But JavaScript doesn't stop. It moves on, and processData
is only run when fetchData is done. So we didn't have to wait for fetchData
to finish before moving on. That's the power of asynchronous JavaScript with callbacks!
But callbacks can become messy when you have callbacks inside callbacks inside callbacks, a situation humorously referred to as âcallback hell.â Thankfully, JavaScript has a cleaner way to handle these scenarios â Promises.
Promises
Promises in JavaScript are objects representing a value that may not be available yet but will be resolved at some point in the future or rejected outright.
In a sense, a Promise is like a delivery thatâs on its way â it could either arrive successfully (resolved) or get lost in transit (rejected).
Hereâs an example:
let deliveryPromise = new Promise(function(resolve, reject) {
// Simulate a delivery delay
setTimeout(function() {
let packageLost = false;
if (!packageLost) {
resolve('Package delivered!');
} else {
reject('Package lost');
}
}, 2000);
});
deliveryPromise.then(function(message) {
console.log(message); // Package delivered! (after 2 seconds)
}).catch(function(error) {
console.log(error); // Package lost (if packageLost is true)
});
In this snippet, deliveryPromise
is a Promise. After 2 seconds, if the packageLost
variable is false
, the Promise gets resolved with the message âPackage delivered!â.
If packageLost
is true
, it gets rejected with the message âPackage lostâ. We handle these outcomes with the then
method (for resolution) and catch
method (for rejection).
What about doing something whether the Promise gets resolved or rejected? Thereâs the finally
method.
deliveryPromise.finally(function() {
console.log('End of delivery attempt');
});
Here, the message âEnd of delivery attemptâ will be logged whether the deliveryPromise
was resolved or rejected.
Chaining Promises
One of the advantages of Promises is that they can be chained. This means you can link multiple Promises, and they will be executed one after the other. Hereâs an example:
let cleanRoom = function() {
return new Promise(function(resolve, reject) {
resolve('Room cleaned');
});
};
let removeGarbage = function(message) {
return new Promise(function(resolve, reject) {
resolve(message + ', garbage removed');
});
};
let winIcecream = function(message) {
return new Promise(function(resolve, reject) {
resolve(message + ', won ice cream');
});
};
cleanRoom().then(function(result){
return removeGarbage(result);
}).then(function(result){
return winIcecream(result);
}).then(function(result){
console.log(result + ', finished');
});
In this snippet, cleanRoom
, removeGarbage
, and winIcecream
are Promises. Each one of them is dependent on the one before it. Notice how we're able to maintain readability and avoid the infamous âcallback hellâ.
Async/await Syntax
The async/await syntax â a simpler way to work with Promises in JavaScript.
To further simplify asynchronous code, ES2017 introduced async functions and the await keyword. It provides a more synchronous-style way of dealing with Promises.
Hereâs an example:
async function makeDelivery() {
let message = await deliveryPromise;
console.log(message);
}
makeDelivery(); // Package delivered! (after 2 seconds)
In this snippet, makeDelivery
is an async function. Inside it, we're using the await
keyword to pause the function execution until deliveryPromise
is resolved or rejected. If the Promise is resolved, message
gets the resolution value. If it's rejected, an error is thrown.
Synchronous vs. Asynchronous Programming
Conclusion
JavaScriptâs power lies not just in its capabilities but also in its versatility.
With the understanding of asynchronous programming, callbacks, and Promises, you now possess the keys to writing non-blocking, efficient JavaScript code. Remember, practice makes perfect.
So keep experimenting with these concepts, and in no time, youâll master the art of asynchronicity in JavaScript!
Feel free to connect with me on Twitter or LinkedIn.
Top comments (0)