Synchronous and Asynchronous in JavaScript
JavaScript is a powerful and versatile language that is used all over the web, from small websites to large applications. It is essential for developers to understand the differences between synchronous and asynchronous code to create and manage websites effectively. Synchronous and asynchronous code are different ways of performing tasks in JavaScript. Synchronous code runs one statement at a time, meaning it runs in sequence and one statement needs to finish before the next begins. Asynchronous code, on the other hand, allows different parts of the code to run at the same time and is a must-have for modern web development. In this article, we will go over the differences between synchronous and asynchronous code in JavaScript, and discuss the advantages and disadvantages of each. We will also go over how synchronous and asynchronous code can be used in practice, giving you the tools you need to create powerful and efficient JavaScript applications.
Synchronous in JavaScript
Synchronous in JavaScript refers to code that is executed in a blocking manner. This means that the code is executed one statement at a time, and the program will not move on to the next statement until the current one has been completed. Synchronous code is executed in the order that it appears in the program and will block the execution of any other code until it has been completed.
Here's an example of synchronous code in JavaScript that uses a for loop to iterate through an array of numbers and print each one to the console.
Example 1:
let numbers = [9, 7, 5, 3, 1];
for (let i = 0; i < numbers.length; i++) {
console.log(numbers[i]);
}
console.log("The loop has finished.");
In this example, the for loop will iterate through the array of numbers and print each one to the console before moving on to the next iteration. The program will not move on to the final console.log statement until the for loop has been completed. The output will be:
Output:
9
7
5
3
1
The loop has finished.
Another example of synchronous code is using the ‘.forEach’ method in JavaScript, as it also blocks the execution of the rest of the code.
Example2:
let numbers = [9, 7, 5, 3, 1];
numbers.forEach(function(num) {
console.log(num);
});
console.log("The loop has finished.");
This will also produce the same output as the previous example.
Output:
9
7
5
3
1
The loop has finished.
Here is another example of synchronous code in JavaScript that demonstrates blocking behavior. This example uses a function that performs a time-consuming task, such as a long-running calculation or a network request, and blocks the execution of the rest of the code until it completes.
Example 3:
function timeConsumingTask() {
let sum = 0;
for (let i = 0; i < 1000000000; i++) {
sum += i;
}
return sum;
}
console.log("Starting task...");
let result = timeConsumingTask();
console.log("Task complete.");
console.log("The result is: " + result);
In this example, the timeConsumingTask function performs a long-running calculation and returns the result. When the timeConsumingTask is called, the program will block the execution of the rest of the code until the function completes. This means that the program will not execute the next console.log statement ("Task complete.") until the timeConsumingTask has finished. The output will be.
Output:
Starting task...
Task complete.
The result is: 499999999500000000
As we can see, the program will not move on to the next statement until the time-consuming task is finished. This is an example of synchronous code in JavaScript. This can lead to freezing the UI or making the application unresponsive if the task is not finished quickly.
Synchronous programming is the default behavior of JavaScript. It can be used to write code that is clear, concise, and reliable.
Asynchronous in JavaScript
Asynchronous programming is a form of coding that allows for non-blocking operations in JavaScript. In this way, the code is able to continue running regardless of whether another part of the code has yet to finish executing. This is done by using functions and other events to carry out operations after a certain point has been reached. Asynchronous programming is sometimes referred to as “non-blocking I/O” because it is used in modern web programming to process user input and output without waiting until the entire program is finished.
JavaScript provides several ways to execute code asynchronously, such as using ‘setTimeout’, ‘setInterval’, ‘XMLHttpRequest’, and ‘Promises’.
• setTimeout
JavaScript's setTimeout function enables you to have a function execute after a predetermined period of time.
An example of asynchronous code in JavaScript is using the ‘setTimeout’ function to delay the execution of a function by a certain amount of time.
Example 1:
console.log("Starting...");
setTimeout(function() {
console.log("After 2 seconds, this notice will show up.");
}, 2000);
console.log("Ending...");
In this example, the ‘setTimeout’ function will delay the execution of the function passed to it by 2 seconds. The program will not wait for the function to complete before moving on to the next statement. The output will be:
Output:
Starting...
Ending...
After 2 seconds, this notice will show up.
As we can see, the program continues to execute the next statement ("Ending…") while waiting for the setTimeout function to complete. This is an example of asynchronous code in JavaScript.
• setInterval
A JavaScript setInterval method enables you to run a function repeatedly at predetermined intervals.
Here's an example of using the ‘setInterval’ to print the current time to the console every second.
Example 2:
console.log("Starting timer...");
let intervalId = setInterval(() => {
let date = new Date();
console.log("The current time is: " + date.toLocaleTimeString());
}, 1000);
In this example, the ‘setInterval’ function is used to call a callback function (anonymous function) which will be executed every 1000 milliseconds (1 second) and it returns an interval ID that can be used to stop the interval.
This code will print the current time to the console every second, until the interval is cleared using ‘clearInterval(intervalId)’.
setTimeout(() => {
console.log("Stopping timer...");
clearInterval(intervalId);
}, 10000);
In this example, the ‘setTimeout’ function is used to stop the interval after 10 seconds.
Output:
Starting timer...
The current time is: 11:33:00 AM
The current time is: 11:33:01 AM
The current time is: 11:33:02 AM
...
Stopping timer...
• XMLHttpRequest
XMLHttpRequest (XHR) is an API in JavaScript that allows you to send HTTP requests and handle the responses asynchronously. It's often used to make API calls, fetch data from a server or upload files.
Here's an example of using ‘XMLHttpRequest’ to make a GET request to a JSON API.
Example 3:
let xhr = new XMLHttpRequest();
xhr.open("GET", "https://www.example.com/path/to/resource");
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
let data = JSON.parse(xhr.responseText);
console.log(data);
}
};
xhr.send();
The "XMLHttpRequest" object is initially created in the example above, and the "open" method is then used to define the GET request type and the API URL. The ‘onreadystatechange’ event is then used to handle the response from the server. The ‘readyState’ property of the XHR object indicates the current state of the request (4 means the request is complete) and the ‘status’ property indicates the HTTP status code of the response (200 means success).
In the example above, once the request is completed and the status is 200, the response text is parsed and logged to the console.
You can also use XMLHttpRequest to send POST, PUT, DELETE request by using ‘xhr.open("POST", "your API url")’ or ‘xhr.open("PUT", "your API url")’ or ‘xhr.open("DELETE", "your API url")’ and you can add data to be sent with the request by using the ‘xhr.send(data)’ method.
Note that there is another way to perform HTTP requests using fetch API, which is a more modern and simpler way of doing it, it returns a promise and you can use .then to handle the response.
• Promise
The eventual success (or failure) of an asynchronous action and its outcome are represented by an object called a promise in JavaScript.
Here's an example of using ‘Promise’ to load an image asynchronously.
Example 4:
let myImage = new Image();
let promise = new Promise((resolve, reject) => {
myImage.onload = resolve;
myImage.onerror = reject;
myImage.src = "https://www.example.com/path/to/resource";
});
promise
.then(() => {
console.log("Image loaded successfully!");
})
.catch(() => {
console.error("Error loading image.");
});
In this example, we first create an instance of the ‘Image’ object. Then we create a new ‘Promise’ object and pass in a function that takes two arguments, ‘resolve’ and ‘reject’.
The ‘resolve’ function is called when the image is loaded successfully and the ‘reject’ function is called when there is an error loading the image.
We then set the ‘src’ property of the ‘Image’ object to the URL of the image we want to load.
We are using the ‘then’ method to attach a callback function that is called when the promise is resolved (the image is loaded) and the ‘catch’ method to attach a callback function that is called when the promise is rejected (there is an error loading the image).
The output will be "Image loaded successfully!" if the image is loaded successfully, otherwise it will be "Error loading image."
Promises are a more elegant way of handling asynchronous operations as they make the code more readable and maintainable. Moreover, using the' then' method allow chaining multiple asynchronous operations together.
You can also use ‘async/await’ to make the code more readable and make it look like synchronous code.
Advantages of working with Synchronous and Asynchronous Code
Synchronous JavaScript code is executed in a linear, top-to-bottom fashion. This means that one line of code will be executed before the next one starts. This can make it easier to understand the reason about the flow of your program, as the order of execution is clear.
Asynchronous JavaScript code allows for multiple tasks to be executed in parallel, rather than one after the other. This can be useful for tasks such as making network requests or reading from a file, as the program can continue to execute other code while it is waiting for a response. Asynchronous code can also help preventing the browser from freezing, as the browser can continue to respond to user input while it is waiting for a response.
Disadvantages of working with Synchronous and Asynchronous Code
The main disadvantage of working with synchronous JavaScript is that it can lead to blocking of the program execution, also known as "blocking the event loop". This occurs when a synchronous task takes a long time to complete, such as a large computation or a network request with a slow response time. During this time, the program is unable to execute any other code, and the browser may appear unresponsive to the user. This can lead to a poor user experience and can make the program less efficient.
The main disadvantage of working with asynchronous JavaScript is that it can make the code more complex and harder to understand, particularly when dealing with a large number of asynchronous tasks. Because asynchronous code relies on callback functions and promises, it can be more difficult to reason about the flow of execution, and it can be easy to introduce bugs such as callback hell, which is a situation where the code becomes hard to understand and manage due to the number of nested callbacks.
Another disadvantage of Asynchronous JavaScript is that it may lead to a race condition, which is a situation where the outcome of a program depends on the relative timing or interleaving of multiple threads. This can lead to unexpected results and hard to debug, as the program may behave differently depending on the order in which the tasks are executed.
Conclusion
In conclusion, both synchronous and asynchronous JavaScript have their own advantages and disadvantages. Synchronous code is easier to understand and reason about, but can lead to blocking of the program execution and a poor user experience. Asynchronous code allows for multiple tasks to be executed in parallel, leading to better performance and a more responsive user interface. Still, it can make the code more complex and harder to understand, particularly when dealing with a large number of asynchronous tasks. It can also lead to a race condition.
When choosing between synchronous and asynchronous JavaScript, it is important to consider the specific requirements of the program and the potential impact on performance, user experience, and code complexity. A proper balance between the two can lead to better program efficiency and user experience.