Exploring Concurrency in JavaScript: Multi-threading with a Single-Threaded Model

Twiter
Facebook
LinkedIn
Email
WhatsApp
Skype
Reddit

The popular computer language JavaScript is used to create web apps. It can only carry out one task at a time because it was first developed as a single-threaded language. Due to this restriction, it was challenging to complete demanding or time-consuming operations, such as sorting a sizable dataset or processing a video. Concurrency was introduced by JavaScript to let developers get over this restriction and do numerous activities at once. We will examine concurrency in JavaScript in this post, including how it functions and how to implement it using a single-threaded model.

Concurrency 

Software frequently has to be able to carry out certain commands deviating from the usual flow without causing other portions of the program to malfunction. If we look more closely, we will discover that these two events happen simultaneously and in parallel, much like how you may see and hear simultaneously. The combined effect of both “programs” helps us understand what is happening. Similar to this, we software engineers want our applications to act similarly.

JavaScript was not designed to use more than one core; instead, it typically operates in a single thread, with each statement executed before the next. It is common for a computer’s CPU to expose more than one core on which we can execute or run commands. This method is reasonable, however as we noted before, there are times when it is necessary to “move” on to the next task before the immediate one is finished.

Thankfully, this isn’t the case and we handle data fetching and other similar actions asynchronously. If we couldn’t, then our users wouldn’t have a good user experience. Certain things, like fetching data, could cause glitches, especially if your internet isn’t fast. We will now examine JavaScript’s concurrency model implementation in more detail. 

Single-Threaded Model

TS0c oCz7qTJNMKxUKe41U Xqavy1qL73ONQGfcQG07dlax Exfa MBRI3nobRYnwDl9uTExA3pP24rFysUsXhIdGt44PqnTiiLNTv Mq 6OhZWQmRySJRIp - Exploring Concurrency in JavaScript: Multi-threading with a Single-Threaded Model

JavaScript is a single-threaded language, which means that it can only execute one task at a time. This is because JavaScript was designed to run inside web browsers, where it is responsible for updating the user interface and handling events such as mouse clicks and key presses. If JavaScript were multi-threaded, there could be issues with concurrency and race conditions, which would make it difficult to maintain consistent behavior across different browsers and platforms.

Even though JavaScript only supports a single thread, there are some benefits to this. For instance, it makes it simpler to reason about code and the programming model. Additionally, it aids in preventing conflicts between various processes or threads that are concurrently attempting to access the same data.

However, JavaScript’s single-threaded nature can affect performance when carrying out difficult or time-consuming tasks. When a JavaScript function takes a long time to complete, the entire user interface may become blocked, giving the impression that the application is sluggish and unresponsive. Use strategies like asynchronous programming, web workers, and other methods to offload intensive processing to different threads or processes whenever possible to prevent this. These methods can aid in enhancing performance and ensuring that the user interface stays responsive even when carrying out difficult or time-consuming operations.

Implementing Concurrency in JavaScript

In this section, we’ll look at how to use the single-threaded model to achieve concurrency in JavaScript.

Callbacks 

When a task is finished, callback functions are called. The simplest approach to implement concurrency in JavaScript is with callbacks. When the task is finished, the callback function, which was passed as an argument to the asynchronous function, is called. Callbacks can be layered, enabling the sequential execution of several asynchronous operations.

Here is an illustration of how to implement concurrency using callbacks:

JavaScript
function getData(url, callback) {

  const xhr = new XMLHttpRequest();

  xhr.open('GET', url);

  xhr.onload = function() {

    callback(xhr.responseText);

  };

  xhr.send();

}

getData('https://jsonplaceholder.typicode.com/posts/1', function(data) {

  console.log(data);

});


The getData function in this illustration accepts two arguments: a URL and a callback procedure. The GET request is sent to the URL after the creation of an XMLHttpRequest object. The callback function is invoked along with the response information when the response is received.

Promises

Promises and callbacks are comparable to one another in that both can be used to manage asynchronous processes in JavaScript. Callbacks were used to handle tasks asynchronously before promises were adopted. But dealing with multiple nested callbacks became challenging and produced callback hell, adding needless complexity to the program. Promises evolved as the best method for managing multiple nested callbacks.

Promise Syntax

There are three states in the Promise syntax: pending, fulfilled, and rejected. A promise can move from the unfulfilled or rejected states after beginning in the pending state. Promise fulfillment indicates the successful completion of the asynchronous process. A promise being turned down indicates that an asynchronous operation failed.

The function with the two arguments, “resolve” and “reject,” is the only argument accepted by the Promise constructor. When an asynchronous operation succeeds, the “resolve” function is called; conversely, when an asynchronous operation fails, the “reject” function is invoked.

Here is an illustration of concurrency being achieved using promises:

JavaScript
function getData(url) {

  return new Promise(function(resolve, reject) {

    const xhr = new XMLHttpRequest();

    xhr.open('GET', url);

    xhr.onload = function() {

      if (xhr.status === 200) {

        resolve(xhr.responseText);

      } else {

        reject(xhr.statusText);

      }

    };

    xhr.onerror = function() {

      reject('Network error');

    };

    xhr.send();

  });

}

getData('https://jsonplaceholder.typicode.com/posts/1')

  .then(function(data) {

    console.log(data);

  })

  .catch(function(error) {

    console.log(error);

  });


The ‘getData’ function in this illustration returns a fresh Promise object. ‘resolve’ and ‘reject’ are the two arguments that the Promise constructor accepts for a function. An XMLHttpRequest object is created inside the function, and a GET request is then made to the URL. The’resolve’ function is called with the response data when the response is received. The’reject’ function is invoked with an error message if the request is unsuccessful.

The promise’s fulfillment is handled by the ‘then’ method. The rejection of the promise is handled using the “catch” method.

Share The Blog With Your Friends
Twiter
Facebook
LinkedIn
Email
WhatsApp
Skype
Reddit

Leave a Reply

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

Advanced topics are covered in our ebooks with many examples.

Recent Posts

pexels-abet-llacer-919734
The Ultimate Dockerfile Keywords
pexels-abet-llacer-919734
How is Docker different from a virtual machine?
uml
Visualizing OOP with UML: A Guide to Unified Modeling Language
dipendencyInjector
Dependency Injection: Achieving Loose Coupling and Testability in OOP