Do JavaScript Promises operate asynchronously?

Can we clarify if JavaScript Promise is asynchronous? I've been researching Promise and async programming, particularly with ajax requests. If Promise isn't inherently async, how can we make it so?

For instance, consider a function that encapsulates another function f along with an argument array args within a Promise. The nature of f itself isn't asynchronous.

function getPromise(f, args) {
 return new Promise(function(resolve, reject) {
  var result = f.apply(undefined, args);
  resolve(result);
 });
}

In order to introduce asynchronicity, I came across recommendations on using the setTimeout method to prevent blocking in code.

function getPromise(f, args) {
 return new Promise(function(resolve, reject) {
  setTimeout(function() { 
   var r = f.apply(undefined, args);
   resolve(r);
  }, 0);
 });
}

Could the utilization of setTimeout indeed render the code non-blocking within the context of a Promise?

(Please note that no third-party Promise APIs are being relied upon, only those supported by browsers).

Answer №1

It seems like there may be a misunderstanding here. JavaScript code is always* blocking because it operates on a single thread. However, the asynchronous coding style in JavaScript allows for external operations like I/O to occur without blocking the main thread. It's important to note that even though these operations are asynchronous, the callback handling the response still blocks other JavaScript from running concurrently.

* With the exception of running multiple processes or using WebWorkers in a browser context.

Now, onto your specific questions:

Just to clarify: is JavaScript Promise asynchronous?

No, the callback provided to a Promise is executed immediately and synchronously. However, you can use Promises to handle asynchronous tasks such as timeouts or file writes, allowing you to wait for those tasks to complete before resolving the Promise.

Will using setTimeout inside a Promise make the code non-blocking?

No, using setTimeout in this way simply changes the order of execution. The rest of your script will continue to run until completion before the setTimeout callback is triggered.

For further clarification:

    console.log( 'a' );
    
    new Promise( function ( ) {
        console.log( 'b' );
        setTimeout( function ( ) {
            console.log( 'D' );
        }, 0 );
    } );

    // Other synchronous stuff, that possibly takes a very long time to process
    
    console.log( 'c' );

In this example, the output will always be:

a
b
c
D

The setTimeout callback will only execute after everything else has completed, meaning it does not make the code truly non-blocking.

Answer №2

Although this question is 7 years old, it's still relevant:

Solution:

  • The Promise block operates synchronously
  • The Promise callback blocks (resolve, reject) are synchronous
  • The call callback (not the block itself) can be asynchronous if a setTimeout block is used within the Promise block

Therefore, to directly address Jane Wayne's inquiry, using a setTimeout block will render the Promise block non-blocking.

Clarification:

Javascript functions synchronously with just one thread. The Javascript engine employs a stack called the Call Stack to execute tasks in Last In First Out (LIFO) order.

On another note, there exist Web APIs within the browser environment, such as setTimeout found within the Window object.

Communication between these two worlds is facilitated by two queues - Microtask and Macrotask queues, that follow a First In First Out (FIFO) model, managed by the Event Loop running continuously.

Event Loop, Microtask and Macrotask queues

The process unfolds as follows:

  • Execute all script through the Call Stack
  • The Event Loop constantly checks if the Call Stack is empty
  • If an empty Call Stack is detected, callbacks from the Microtask queue are pushed to the Call Stack
  • Once the Microtask queue is cleared, the Event Loop works on the Macrotask queue

Now, delving into the world of Promises:

  • The Promise block executes synchronously
  • Within the Promise block, anything executed outside the Javascript engine (like a setTimeout block) is deemed asynchronous since it runs outside the main Javascript thread.
  • While the execution of Promise callbacks (resolve and reject) may seem asynchronous, the core block remains synchronous

Hence, without any asynchronous components inside the Promise block or its callbacks, the Promise tends to halt your execution.

For instance:

console.log(`Start of file`)

const fourSeconds = 4000

// Non-blocking execution due to being added to the Macrotask queue
// Microtasks take precedence so all Promises without setTimeout go first
setTimeout(() => {
    console.log(`Callback 1`)
})

// Blocking execution because the Promise block is synchronous
// However, the result will only show after the Call Stack clears
// This becomes the first Microtask printed
new Promise((resolve, reject) => {
    let start = Date.now()
    while (Date.now() - start <= fourSeconds) { }
    resolve(`Promise block resolved`)
}).then(result => console.log(result))

// Non-blocking execution, goes to the Macrotask queue due to setTimeout
// Will pause all subsequent Macrotasks in the queue
// In this scenario, it pauses the printing of Macrotask "Callback 2"
new Promise((resolve, reject) => {
    setTimeout(() => resolve(`Promise callback block resolved`))
}).then(result => {
    let start = Date.now()
    while (Date.now() - start <= fourSeconds) { }
    console.log(result)
})

// Non-blocking execution, added to the Macrotask queue
// Microtasks hold priority over it
// Previous Macrotasks also delay its execution, hence it is last to run
setTimeout(() => {
    console.log(`Callback 2`)
})

// Non-blocking execution, enters the Microtask queue overriding setTimeout
// Previous microtasks have prime importance
Promise.resolve(`Simply resolved`).then(result => console.log(result))

console.log(`End of file`)

/*

Output:
 
Start of file
[... execution blocked 4 seconds ...]
End of file
Promise block resolved
Simply resolved
Callback 1
[... execution blocked 4 seconds ...]
Promise callback block resolved
Callback 2

*/

Answer №3

const p = new Promise((resolve, reject) => {
  if (1 + 1 === 2) {
    resolve("A");
  } else {
    reject("B");
  }
});

p.then((name) => console.log(name)).catch((name) => console.log(name));
console.log("hello world");

Asynchronous behavior of Promises allows for non-blocking execution of subsequent code lines while the promise is in pending state.

Answer №4

Thank you for the helpful MDN reference! Running this code will display output asynchronously.

================================================================ asynchronous implementation using Promises

const log = console.log;

//---------------------------------------

class PromiseLab {

    play_promise_chain(start) {
        const promise = new Promise((resolve, reject) => {      
            resolve(start);
        });     
        promise.then((start) => {
            log(`Value: "${start}" -- adding one`);
            return start + 1;
        }).then((start) => {
            log(`Value: "${start}" -- adding two`);
            return start + 2;
        }).then((start) => {
            log(`Value: "${start}" -- adding three`);
            return start + 3;
        }).catch((error) => {
            if (error) log(error);
        });             
    }
        
}

//---------------------------------------

const lab = new PromiseLab();
lab.play_promise_chain(100);
lab.play_promise_chain(200);

Output should be asynchronous like:

Value: "100" -- adding one
Value: "200" -- adding one
Value: "101" -- adding two
Value: "201" -- adding two
Value: "103" -- adding three
Value: "203" -- adding three

================================================================ Synchronous implementation using custom MyPromise class

const log = console.log;

//---------------------------------------

class MyPromise {
    
    value(value) { this.value = value; }
    
    error(err) { this.error = err; }
    
    constructor(twoArgFct) {
        twoArgFct(
            aValue => this.value(aValue),
            anError => this.error(anError));    
    }
    
    then(resultHandler) { 
        const result = resultHandler(this.value);
        return new MyPromise((resolve, reject) => {     
            resolve(result);
        });
    }
    
    catch(errorHandler) { 
        errorHandler(this.error());
    }
    
}

//--------------------------------------
    
class MyPromiseLab {

    play_promise_chain(start) {
        const promise = new MyPromise((resolve, reject) => {        
            resolve(start);
        });     
        promise.then((start) => {
            log(`Value: "${start}" -- adding one`);
            return start + 1;
        }).then((start) => {
            log(`Value: "${start}" -- adding two`);
            return start + 2;
        }).then((start) => {
            log(`Value: "${start}" -- adding three`);
            return start + 3;
        }).catch((error) => {
            if (error) log(error);
        });             
    }
        
}

//---------------------------------------

const lab = new MyPromiseLab();
lab.play_promise_chain(100);
lab.play_promise_chain(200);

Output should be synchronous:

Value: "100" -- adding one
Value: "101" -- adding two
Value: "103" -- adding three
Value: "200" -- adding one
Value: "201" -- adding two
Value: "203" -- adding three

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Using JavaScript to assign multiple classes to a function at separate intervals

I'm trying to add two classes to a JavaScript function, but I'm encountering some issues. When I try to add both classes in the same line, only one of them works. However, if I add each class on separate lines within the function, only the second ...

Ways to focus on a specific div using JavaScript

I am attempting to create a 'snow' effect on the background of a particular div with a red border. Here is the code, but you can also view it here: <!-- language: lang-js --> var width = getWidth(); var height = getH ...

Obtain the total views for a particular map

Looking to optimize my use of the Google Maps JavaScript API. Any suggestions on where I can find information on tracking views and SEO for my map? Appreciate any assistance you can provide. ...

Shorten the content while preserving the HTML using JavaScript

When printing a string containing HTML links, I need to truncate the visible text while preserving the link structure. Simply using a substring method would disrupt the HTML tags in the string. I aim to display only 100 characters but also remove any inc ...

Integrating a search box with radio buttons in an HTML table

I am currently working on a project that involves jQuery and a table with radio buttons. Each button has different functionalities: console.clear(); function inputSelected(val) { $("#result").html(function() { var str = ''; ...

Jest may come across test suites, but it discreetly disregards the individual tests

Having encountered an issue with Jest testing in a Nuxt/Vue v2 project, I found that after making some changes, the tests were no longer running. The unit tests were either passing or failing initially, but suddenly stopped running altogether. ----------|- ...

Attempting to scroll through a webpage and extract data using Python and Selenium in a continuous manner

Recently, I posed a question (you can find it here: Python Web Scraping (Beautiful Soup, Selenium and PhantomJS): Only scraping part of full page) that shed light on an issue I encountered while attempting to scrape all the data from a webpage that updates ...

Step-by-Step Guide on Dividing Table Row Data into Two Distinct Tables

At present, I have created a dynamic table that retrieves data from the database using forms in Django. I'm facing an issue with this table as even though there are 7 columns, only 2 of them are visible. However, all 5 hidden columns do contain impor ...

Encountering difficulties with the functionality of Google Places API

I am attempting to incorporate the autocomplete functionality of Google Places API into a text field. Below is the code I have implemented: <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"> </script> <script ...

Access the properties of a JSON object without specifying a key

I am dealing with a collection of JSON arrays structured like this: [ { team: 111, enemyId: 123123, enemyTeam: '', winnerId: 7969, won: 1, result: '', dat ...

hiding html elements by using the display property set to none instead of physically removing

I am currently utilizing an if-else statement to display different HTML structures. As a result, the entire HTML is being rendered twice. Is there a way we can utilize 'display: none' instead? I attempted to use it in th ...

Can you explain the distinction between querying a database and making a request to an endpoint?

Recently, I've been diving into learning mongoose but came across a code that left me puzzled. I'm curious as to why we include the async keyword at the beginning of the callback function when querying a database. Isn't it already asynchron ...

Once the promise program enters the if-condition, even though the condition itself is actually false

There seems to be an issue with retrieving the location code using the AccuWeather API before getting the weather data for a city. Despite the location array being empty, the program still proceeds to a branch that expects a non-empty array. router.get(& ...

I encountered an error while trying to add a document to Firestore using the 'add' method in Vue.js. Can someone provide guidance on how to

Whenever the function is triggered (on click), I aim to include a new document. Although it functions with .set(), my goal is for a new document to be created each time the form is submitted. The current error code I am encountering is: I initially suspe ...

update the element that acts as the divider in a web address (Angular)

Is it possible to modify the separator character used when obtaining the URL parameters with route.queryParams.subscribe? Currently, Object.keys(params) separates the parameters using "&" as the separator. Is there a way to change this to use a differe ...

Is there a way for me to instruct jQuery to revert an element back to its initial value, prior to any dynamic alterations?

My question is regarding changing the content of a div using the `html()` method in JavaScript. I am currently working on implementing a "cancel" button and would like to revert the content back to its original value without having to completely reset it w ...

Chrome displaying an extJs Button image

Could it be that Chrome is evolving into the new IE in terms of CSS issues? Here is the code I have for creating ExtJS buttons within an accordion: var button = Ext.create('Ext.Button', { text: '<img src="'+resp.sellers.externa ...

Intermittent occurrence of (404) Not Found error in the SpreadsheetsService.Query function

Using the Spreadsheet API, I frequently update various sheets. Occasionally, and without any pattern, the SpreadsheetsService.Query function returns a (404) Not Found error. This issue does not seem to be related to internet connectivity or server downti ...

Best method to generate an element using any jQuery selector string

What I want to accomplish I am looking to create an element that matches any given selector string. Here's a quick example: var targetString = "a.exaggerated#selector[data-myattr='data-here']"; var targetEl = $(targetString); if(!targetE ...

The Chrome Extension was denied from loading due to a violation of the specified Content Security Policy

I am encountering an issue while loading a Chrome Extension using React. Whenever I try to load it, the error message below pops up: Refused to load the script 'https://code.jquery.com/jquery-3.2.1.slim.min.js' because it violates the following ...