Home > front end >  Await multiple promises to resolve while returning the value of each promise as soon as it is resolv
Await multiple promises to resolve while returning the value of each promise as soon as it is resolv

Time:10-03

The problem

While working with a restful API, I had to make multiple requests to retrieve data around a single search. The problem that I seem to be facing is that as the results are returned from a large database, some promises take FOREVER to resolve.

Current solution

Currently I make all the requests in a loop while adding the promises to an array and then using await Promise.all() to wait for them to be resolved but this makes loading times > 30 seconds at times even when the earliest promise resolved within a few seconds.
I am looking for a way that I can 'Lazy Load' the results in. I do have access to the restful server so any changes in either the front-end or back-end would help however I would prefer the changes to be at the front end.

CodePudding user response:

You can handle the individual results as you're populating the array for Promise.all, which you can use to know when they've all finished.

It's tricky without any code in the question to work with, but here's a simple example:

const promises = [1, 2, 3, 4, 5].map(async (num) => {
    const result = await doRequest(num);
    console.log(`Immediate processing for "${result}"`);
    return result; // If you're going to use the contents of the array from `Promise.all`'s fulfillment
});
const allResults = await Promise.all(promises);
console.log(`All processing done, all results:`);
for (const result of allResults) {
    console.log(result);
}

Live Example:

const rndDelay = () => new Promise((resolve) => setTimeout(resolve, Math.round(Math.random() * 1000)   100));

async function doRequest(num) {
    console.log(`Requesting ${num}`);
    await rndDelay();
    return `Result for ${num}`;
}

async function main() {
    const promises = [1, 2, 3, 4, 5].map(async (num) => {
        const result = await doRequest(num);
        console.log(`Immediate processing for "${result}"`);
        return result; // If you're going to use the contents of the array from `Promise.all`'s fulfillment
    });
    const allResults = await Promise.all(promises);
    console.log(`All processing done, all results:`);
    for (const result of allResults) {
        console.log(result);
    }
}

main()
.catch((error) => console.error(error));
.as-console-wrapper {
    max-height: 100% !important;
}

Notice that the callback we give map (in this example) is an async function, so it returns a promise and we can use await within it.

If you can't use an async wrapper where you're creating the promises, that isn't a problem, you can fall back to .then:

const promises = [1, 2, 3, 4, 5].map((num) => {
    return doRequest(num)
        .then((result) => {
            console.log(`Immediate processing for "${result}"`);
            return result; // If you're going to use the contents of the array from `Promise.all`'s fulfillment
        });
});
const allResults = await Promise.all(promises);
console.log(`All processing done, all results:`);
for (const result of allResults) {
    console.log(result);
}

const rndDelay = () => new Promise((resolve) => setTimeout(resolve, Math.round(Math.random() * 1000)   100));

async function doRequest(num) {
    console.log(`Requesting ${num}`);
    await rndDelay();
    return `Result for ${num}`;
}

async function main() {
    const promises = [1, 2, 3, 4, 5].map((num) => {
        return doRequest(num)
            .then((result) => {
                console.log(`Immediate processing for "${result}"`);
                return result; // If you're going to use the contents of the array from `Promise.all`'s fulfillment
            });
    });
    const allResults = await Promise.all(promises);
    console.log(`All processing done, all results:`);
    for (const result of allResults) {
        console.log(result);
    }
}

main()
.catch((error) => console.error(error));
.as-console-wrapper {
    max-height: 100% !important;
}

CodePudding user response:

Use Promise.race

The Promise.race() method returns a promise that fulfills or rejects as soon as one of the promises in an iterable fulfills or rejects, with the value or reason from that promise.

Promise.any is similar, but:

Also, unlike Promise.race(), which returns the first settled value (either fulfillment or rejection), this method returns the first fulfilled value. This method will ignore all rejected promises up until the first promise that fulfills.

Here's some pseudo-code

create array of promises
while array is not empty
  oneResolvedPromise = Promise.race(theArray)
  remove oneResolvePromise from the array
  • Related