What is the best way to ensure the correct resolution order of several promises?

Exploring the realm of modern JS and diving into ECMAScript 6 Promises. Experimenting with a simple test:

let slow = new Promise((resolve) => {
  setTimeout(function()
  {  
console.log('slow');
resolve();
  }, 2000, 'slow');
});

let instant = new Promise((resolve) => {
console.log('instant');
resolve();
});

let quick = new Promise((resolve) => {
  setTimeout(function()
  {  
console.log('quick');
resolve();
  }, 1000, 'quick');
});

Promise.all([slow, instant, quick]).then(function(results) {
  console.log('finished');
}, function(error) {
  console.log(error);
});

Desiring to ensure that "instant" is not logged before "slow" completes. Aiming for the console output to be: "quick", "slow", "instant", and "finished". They must all start asynchronously at the same time.

How can I make this happen?

Answer №1

To clarify, your goal is to initiate all promises simultaneously and showcase the results of each promise in a specific order as they are received, right?

In that scenario, my approach would be:

let slow = new Promise((resolve) => {
  setTimeout(function()
  {
    // Instead of logging here, we resolve with the desired value
    resolve('slow');
  }, 2000, 'slow');
});

let instant = new Promise((resolve) => {
    resolve('instant');  
});

let quick = new Promise((resolve) => {
  setTimeout(function()
  {  
    resolve('quick');  
  }, 1000, 'quick');
});

// All Promises are now running. Let's display the results...

// First, wait for the result of `slow`...
slow.then((result) => {
  // Result received...
  console.log(result);
  
  // Now await the result of instant...
  instant.then((result) => {
    
    // Result obtained...
    console.log(result);
    
    // Now await the result of quick...
    quick.then((result) => {
      
      // Result acquired...
      console.log(result);
      
    }).then((result) => {
      // Completed
      console.log('finished');
    });
  });
});

Note that unlike cchamberlain's response, this method does not necessitate waiting for all promises to resolve prior to returning results. It returns the results as they arrive, while maintaining the specified order of results. (To confirm this, adjust the waiting time of quick to 2500ms, and observe that its result is displayed 500ms after instant.) Depending on your application, this may be beneficial.

The above code may seem cluttered, but fortunately, it can be significantly streamlined using the new async/await syntax in ES2017:

let slow = new Promise((resolve) => {
  setTimeout(function()
  {
    // Instead of logging here, we resolve with the desired value
    resolve('slow');
  }, 2000, 'slow');
});

let instant = new Promise((resolve) => {
    resolve('instant');  
});

let quick = new Promise((resolve) => {
  setTimeout(function()
  {  
    resolve('quick');  
  }, 1000, 'quick');
});

// All Promises are now running. Let's show the results...

async function logResults(...promises) {
  for (let promise of promises) {
    console.log(await promise);
  }
}

logResults(slow, instant, quick).then(() => console.log('finished'));

Test in Babel. Note: Presently, the above code won't work in modern browsers without Babel support (as of October 2016). In future browsers, it will.

Answer №2

The problem arises because the logging occurs within the setTimeout function, rather than directly after resolving the promise.

const sluggish = new Promise((resolve) => {
  setTimeout(() => {
    console.log('sluggish - from setTimeout');
    resolve('sluggish - from resolve');
  }, 2000, 'sluggish');
});

const immediate = new Promise((resolve) => {
  console.log('immediate - from setTimeout');
  resolve('immediate - from resolve');
});

const speedy = new Promise((resolve) => {
    setTimeout(() => {
    console.log('speedy - from setTimeout');
    resolve('speedy -from resolve');
  }, 1000, 'speedy');
});

Promise.all([sluggish, immediate, speedy]).then((output) => {
  console.log('output -', output);
  console.log('done');
}, (error) => {
  console.log(error);
});

Supplying a value to the resolve method will aggregate all results in the Promise.all. The responses return as an array from each promise, allowing for iteration upon completion of all promises.

Answer №3

UPDATED

In order to ensure that the results are logged in the correct order, you cannot simply kick them all off at the same time with your current code. One approach could be to schedule each promise and then print the results upon completion:

let slow = new Promise((resolve) => {
  setTimeout(() => resolve('slow'), 2000)
})

let instant = new Promise((resolve) => {
  // Consider using setImmediate for async execution in future updates
  setTimeout(() => resolve('instant'), 0)
})

let quick = new Promise((resolve) => {
  setTimeout(() => resolve('quick'), 1000)
})

Promise.all([slow, instant, quick]).then(function(results) {
  for(let result of results) {
    console.log(result)
  }
  console.log('finished')
}, function(err) {
  console.error(err)
})

This scheduling approach ensures that the results are printed in the expected order.

Answer №4

If you need to address the requirements mentioned in Question:

"instant" should not echo/log before "slow"

and

all elements must start at the same time asynchronously.

You can simply rearrange the elements within the iterable object passed to Promise.all(), or modify the resulting array within the .then() chain of Promise.all() before using console.log() on each element of the resulting array.

If the requirement is:

"How do I wait for another promise?"

or

"How do I ensure the order of resolution for multiple promises?"

refer to this specific Answer.


Promise.all collects an array of values from all promises within the iterable object it was provided. The resultant array maintains the order of the original iterable object, not based on the sequence of promise resolutions. If any item in the iterable array is not a promise, it is treated as one by Promise.resolve.

let slow = new Promise((resolve) => {
  setTimeout(function(value) {  
resolve(value);
  }, 2000, "slow");
});

let instant = new Promise((resolve) => {
resolve("instant");
});

let quick = new Promise((resolve) => {
  setTimeout(function(value) {  
resolve(value);
  }, 1000, "quick");
});

Promise.all([slow, instant, quick]).then(function(results) {
  console.log("finished");
  console.log(results.join("\n"))
}, function(error) {
  console.log(error);
});

Answer №5

At the outset, your code initiates all 3 promises simultaneously. The logging of "finished" is also done correctly. Based on my understanding of the question, you aim to process the results of the promises in a sequential manner while executing them concurrently.

let slow = new Promise((resolve) => {
  setTimeout(function()
  {  
    resolve();
  }, 2000);
});

let instant = new Promise((resolve) => {
    resolve();
});

let quick = new Promise((resolve) => {
  setTimeout(function()
  {  
    resolve();
  }, 1000);
});

instant.then(function(results) {
  console.log("instant");
}).then(function(){return quick;}).then(function(results) {
  console.log("quick");
}).then(function(){return slow;}).then(function(results) {
  console.log("slow");
}).then(function(){ return Promise.all([slow, instant, quick]);}).then(function(results) {
  console.log('finished');
}).catch(function(error) {
  console.log(error);   
});

This approach ensures that you handle the resolutions in a specific order.

Note: In your given example, the use of setTimeout guarantees sequential calling based on time order. Therefore, your current code will already log "instant", "quick", "slow", and then "finished". However, the code I've shared secures this order for any assortment of promises with varying resolution times.

Answer №6

Promise.all() is not needed for achieving the desired outcome.

You can accomplish the task by making function calls to return the Promise constructor, passing the necessary value to be logged through resolve() or reject(). Store the values in an array. Use .then() to handle the returned Promise value, add it to the array, and return the array to the callback function parameter in the subsequent .then() of the chain. Retrieve the array of chained Promise values in the final .then() of the chain.

let results = [];

let pace = value => {console.log(value); results.push(value); return results};

let slow = () => new Promise((resolve) => {
  setTimeout((value) => {  
    resolve(value);  
  }, 2000, "slow");
});

let instant = () => new Promise((resolve) => {
    resolve("instant");  
});

let quick = () => new Promise((resolve) => {
  setTimeout((value) => {  
    resolve(value);  
  }, 1000, "quick");
});

slow().then(pace)
.then(instant).then(pace)
.then(quick).then(pace)
.then(res => console.log("All done, results:", res))
.catch(error => console.log(error));

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

Contrast 2 GET objects retrieved from separate controllers

I have 2 collections of data from different controllers. Data Collection 1 (Controller Name): [{id:1,"name":"jakov"},...] Data Collection 2 (Controller Nickname): [{id:1, "nickname" : "jandric", "nameId" :1, "title" : "master"},...] I send data from C ...

Make sure that the iframe loads the next page with enough force to break out

My dilemma involves an iframe that loads the new tab page. When a user clicks on the thumbnail, it opens within the iframe. My goal is to have any subsequent load in the iframe redirected to window.top. Is there a way to achieve this without manually setti ...

Encountering an issue with the history module when utilizing the webpack dev server

I am encountering an issue while trying to run webpack dev server. The history functionality was working fine until I started using the webpack module. A warning message appeared in my console: WARNING in ./src/history.js 2:15-35 export 'createBrows ...

Interaction between the Vue parent and any child component

I am working with a series of components that are structured in multiple levels. Each component has its own data that is fetched over AJAX and used to render child components. For instance, the days parent template looks like this: <template> &l ...

What are the steps for translating multiple meshes in various directions using three.js?

One issue that I am encountering involves creating 100 meshes with a for loop, all of which have the same position coordinates of 0,0,0. I would like these meshes to move in different directions individually. Below is my code for creating the 100 meshes: ...

A step-by-step guide on extracting multiple data entries from JSON that correspond to an array of IDs

Within my JavaScript code, I am working with a JSON dataset containing posts, each with a unique post_id. I also have an array of specific post_ids that I want to use to extract certain posts from the main dataset. const dataset = [ {post_id: 1, titl ...

Error: Trying to modify a property that is set as read-only while attempting to override the toString() function

I have a specific object that includes an instance variable holding a collection of other objects. Right now, my goal is to enhance this list of elements by adding a customized toString() method (which each Element already possesses). I experimented with t ...

CASL user update has been refreshed

My CASL implementation is quite basic and I've noticed that the documentation lacks detail. The code I'm using is essentially a copy-paste from the docs: import { abilitiesPlugin } from '@casl/vue' import defineAbilitiesFor from &apos ...

Setting the base URL in Next.js according to an environment variable: a step-by-step guide

I currently have a Strapi backend and Next.js frontend application deployed on DigitalOcean. Within DigitalOcean, I have configured an environment variable for the frontend: API_URL = ${APP_URL}/api To generate the base URL, I retrieve this variable using ...

What is the distinction between revealing environment variables in Next.js using the next.config.js as opposed to utilizing the NEXT_PUBLIC prefix?

As stated in the nextjs documentation, to make my environment variables accessible in the browser, I can simply prepend them with NEXT_PUBLIC in my .env.local file, like this: NEXT_PUBLIC_VAR=7 However, it seems that another approach is available where I ...

What is the best method to redirect users who are not logged in from every URL when using PassportJs?

I am currently developing a web application using NodeJS, and I have integrated PassportJS with passport-local-mongoose for authentication. Below is the code snippet I've created for the root route to verify if the user is logged in: app.get('/ ...

Creating an interactive map on WordPress: A step-by-step guide

I have successfully created a clickable image on Codepen <div style="width: 1000px; height: 993.73px;"> <img src="https://www.dyfedarchaeology.org.uk/wp/wp-content/uploads/Testmap.svg" alt=&q ...

Tips for accessing Ajax data within Ember computed property

I'm facing a challenge with returning data from an Ajax call in a computed property. Despite being aware of the asynchronous nature, I am unable to figure out how to do it due to the specific requirement of returning the data in an array format with o ...

When trying to use setInterval () after using clearInterval () within an onclick event, the functionality seems

Can anyone assist me with an issue I am encountering while using the setInterval() function and then trying to clear it with clearInterval()? The clearInterval() works fine, but the automatic functionality of li elements with a specific class suddenly stop ...

A guide to setting up checkbox input in Vue 3 so that it toggles between checked and unchecked states when an

I'm in the process of creating a div container that will contain both an image and a checkbox input element. The checkbox input has a click handler that passes a value to a function, as well as a :value binding which sends the value to an array named ...

The upcoming development does not involve creating an entire HTML webpage using on-demand static site generation (SS

I’m encountering a problem when utilizing getStaticPaths and getStaticProps to create an on-demand SSG for a sharing page. My setup involves Next v12.1.0 and React 17.0.2. After building a specific /[id] page, I can retrieve the data but the HTML output ...

Getting callback data from a function in the AWS SDK without using asynchronous methods

I have a code snippet that fetches data from AWS using a function: main.js const { GetInstancesByName } = require("./functions"); var operationmode = "getinstances"; if (operationmode == "getinstances") { let getresult = ...

The organizational structure of data in MongoDB for posts, comments, saved content, and likes

I am currently diving into the world of MEANJS web development and working on structuring my data. As a newcomer to the NoSql concept, I am seeking guidance on the best practices to follow. The data I need to store includes: questions answers likes saved ...

Retrieve JSON data within an HTML element, where the element is sourced from an AJAX GET response

What is the best way to extract JSON data from the response below? $.get('http://localhost:8000/json', function(response) { alert(response); }); The response is as follows: <div id="dom-target" style="display: none;"> {"logo":"log ...

How can I conceal login and register router-links in the Vue + Laravel SPA project navbar immediately after a user logs in?

Currently, I am working on my Bachelor's degree project and have encountered a specific issue. While the login, register, and logout functions all seem to be working well, there is an inconsistency with the navigation bar not automatically switching b ...