Mastering Synchronous & Asynchronous JavaScript: A Newbie's Guide

Unlocking Synchronous & Asynchronous Programming in JavaScript

by — July 28, 2023
Reading Time: 8 mins read

Table of Contents

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.

Understanding 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.

An Overview of Async 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 should you master asynchronicity in 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:

Understanding 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.

Understanding 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.”

The async/await Syntax

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: A Comparison

  Synchronous Programming Asynchronous Programming
Process Executes operations in sequence, one after the other Starts an operation and moves on, comes back when the operation is done
Nature Blocking – waits for each operation to complete before moving to the next one Non-blocking – doesn’t wait for an operation to complete before moving to the next one
Best For Simple scripts, sequential operations where each operation depends on the previous one, server-side rendering Time-consuming operations like network requests, file I/O, user interactions, timers, etc.
Limitations Can lead to performance issues due to its blocking nature Can be harder to understand and manage due to its non-linear execution
JS Constructs Basic JavaScript operations Callbacks, Promises, async/await
Execution Order Deterministic – operations are executed in the order they appear Non-deterministic – operation order depends on when asynchronous operations complete

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!

References/Resources

To deepen your understanding of the topics covered in this article, check out these resources and book recommendations:

Book Recommendations:

Happy learning!

🚀 Before You Go:

  • 💬 Got thoughts? Share your insights!
  • 📤 Know someone who needs this? Share the post!
  • 🌟 Your support keeps us going!

💻 Level up with the latest tech trends, tutorials, and tips - Straight to your inbox – no fluff, just value!

Leave a Reply

Your email address will not be published. Required fields are marked *

Support WebDevStory
Pictory 20% off
The Book Every Programmer Swears By
Recommended Hosting
Earn Money
Upgrade Your Skills
Work Comfortably Anywhere

Note: Some links on this page might be affiliate links. If you make a purchase through these links, I may earn a small commission at no extra cost to you. Thanks for your support!