Promises: combine single resolve methods and Promise.all() - javascript

I have multiple promises fetching different assets. I want to achieve:
Executing some code first to handle some of these promises via then().
When everything is resolved/fetched, executing some code that launches my app via Promise.all().then().
I need to make sure 1 is excuted before 2. It does seem to work from what I have tested.
// handle promises one by one
promise1.then(results => {});
promise2.then(results => {});
// common process that should come after the above then()s
Promise.all([promise1, promise2])
.then(results => {});
But can I rely on it? Are "single" then() always executed before a then() on Promise.all()?

While Promise.all is stalled because it waits for all the promises to finish, there is no guarantee that it will resolve after the .then call on the promise that resolves last.
Instead you should try creating new promises from the .then calls.
// handle promises one by one
const promise3 = promise1.then(results => {});
const promise4 = promise2.then(results => {});
// common process that should come after the above then()s
Promise.all([promise3, promise4])
.then(results => {});
This way you can guarantee that the Promise all will resolve after the then calls

You can around by using Async/await. With waiting for each promise, you can write code to process data at the end.
Example:
(async () => {
const responses = [];
await new Promise((res) => res("promise1")).then((res) => {
console.log("From promise1 first then");
responses.push(res);
});
// Code at the end to do after all promises
console.log("Coming from second");
console.log("Responses:", responses);
})();

Related

JS: async/await for multiple promises

If I have promise running synchronously, how do I want for both of them to finish? Doesn't await only work for a single Promise?
async function testing() {
let promise1 = new Promise((resolve, reject) => {
setTimeout(() => resolve("one"), 1000)
});
let promise2 = new Promise((resolve, reject) => {
setTimeout(() => resolve("two"), 1000)
});
// how do I await both of these?
}
You can use Promise.all to wait for both of them at once
const [result1, result2] = await Promise.all([promise1, promise2]))
If you want to resolve all promises then you can do two things which are Promise.allSettled() or Promise.all(). So based on your need, choose one.
The Promise.allSettled() method returns a promise that resolves after all of the given promises have either fulfilled or rejected, with an array of objects that each describes the outcome of each promise.
Promise.allSettled([
Promise.resolve('promise1'),
Promise.reject('promise2')
]).then(console.log)
It is typically used when you have multiple asynchronous tasks that are not dependent on one another to complete successfully, or you'd always like to know the result of each promise.
In comparison, the Promise returned by Promise.all() may be more appropriate if the tasks are dependent on each other / if you'd like to immediately reject upon any of them rejecting.
Promise.all([Promise1, Promise2])
.then(result) => {
console.log(result)
})
.catch(error => console.log(`Error in promises ${error}`))
Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled
You can use promise.all or promise.allSettled
If your use case is like if any one of the request fails then operation needed to be fail means then its good to use promise.all
If you need to keep waiting for all, regardless of any in between request fails or not then you need to use promise.allSettled.
But both of these wont ensure serializability. If you need to ensure serializable i would prefer to use for await loop and use promise.resolve inside it.

Trouble with resolving promises in sequence

I'm trying to resolve an array of promises in the build process of a NextJS app since I'm getting network errors when using Promise.all, but for some reason I'm having trouble resolving the promises.
This code works, but does not work with my build:
const activityPlacesPromises = activityLocationIDs.map((id) =>
this.get(`places/${id}`)
);
const activityPlaces = await Promise.all(activityPlacesPromises);
console.log(activityPlaces); // Returns the data correctly
This code does not work:
const activityPlaces = activityLocationIDs.reduce((promise, id) => {
return promise.then(() => this.get(`places/${id}`));
}, Promise.resolve());
console.log(activityPlaces); // Returns Promise { <pending> }
Why isn't the Promise.resolve() reduce function working?
PS: I am going off of this SO question: Resolve promises one after another (i.e. in sequence)?
activityPlace is still just a promise you will need to await
console.log(await activityPlaces);
Note that you're not doing anything with the result of each promise (aside from the last one)
Wouldn't it be way easier to just throw this in a regular for loop and await one by one? the reduce pattern is useful for cases where you don't have async/await, but you don't seem to have this limitation:
const results = [];
for(const id of activityLocationIDs) {
results.push(await this.get(`places/${id}`));
}
console.log(result);
The above code matches the behavior of your first sample.
It looks like you are trying to avoid using async/await, firstly perhaps you should catch any errors, to allow logging and execution to continue:
const activityPlaces = activityLocationIDs.reduce((promise, id) => {
return promise
.then(() => this.get(`places/${id}`))
.catch((err) => console.error(`Error getting place ID: ${id}`, err))
}, Promise.resolve());
activityPlaces.then(() => console.log(activityPlaces));
Also, consider that since your code is no longer using async, your build may not wait for the promises to resolve before ending.

Node: Using promise.all() to call API's in parallel?

I'm a bit confused how promise.all work, does it run the array of promises in parallel?
So here is a sample code
// index.js
const getSomething = async (args) => {
return await apiCallHere(args)
}
// Create Array of Promises
const arrayOfPromises = sampleArray.map(sample => new Promise((resolve, reject) => {
try {
const something = this.getSomething(sample, args)
resolve(something)
} catch (error) {
reject(error)
}
}))
await Promise.all(arrayOfPromises)
From what I observed, Promise.all runs the promises in parallel, and wait for all promises
to finish.
does it run the array of promises in parallel
Promise.all doesn't, no; your code does (well, probably; see the Notes below). The work is already underway before Promise.all sees the promises. What Promise.all does is give you a promise that will settle when all of the promises you give it are fulfilled (or one of them is rejected).
It's your code that makes the work run in parallel, by starting the actions that the promises report the completion of (in the map callback) in order to give them to Promise.all in the first place. See *** comments:
// *** `map` is synchronous, it loops all the way through the array
const arrayOfPromises = sampleArray.map(sample => new Promise((resolve, reject) => {
try {
const something = this.getSomething(sample, args) // *** This is what starts each thing
resolve(something)
} catch (error) {
reject(error)
}
}))
// *** The work is already underway here
// *** This just waits for it to finish
await Promise.all(arrayOfPromises)
Remember that a promise is just a way to observe the completion of an asynchronous process. Promises don't run anything. They just report the completion of something, along with the fulfillment value or rejection reason.
Notes
If this.getSomething(sample, args) returns a promise, your code is falling prey to the explicit promise creation anti-pattern: There's no reason to use new Promise here at all. Instead:
const arrayOfPromises = sampleArray.map(sample => this.getSomething(sample, args));
If this.getSomething(sample, args) returns its value immediately, then there's no point in using promises here at all, because the operations are already complete by the time it returns.
(I assume it doesn't start an asynchronous process and report completion via a callback instead of a promise, since you haven't shown a callback but you have shown using the return value.)
The getSomething you've shown in the question returns a promise (because it's an async function), but you wouldn't call that as this.getSomething(...), just as getSomething(...).

Make Promise.race finish only when one is successful

I have three asynchronous functions that return an object. If one of the functions are not successful, they return an empty object. The issue is that if I try to Promise.race these functions, one of the not successful functions may finish first and have an empty object return. My Promise code looks like this:
let product = await Promise.race([
get_info_1(code),
get_info_2(code),
get_info_3(code)
])
Is it possible to stop one of the asynchronous get_info functions from running without stopping the Promise.race? Or could I somehow tell Promise.race to wait until the returned object contains some information?
You can make the rejected Promise never complete
const never_resolve = new Promise(() => {})
{
(async function() {
let product = await Promise.race([
Promise.reject(1).catch(_ => never_resolve),
Promise.resolve(2).catch(_ => never_resolve),
Promise.reject(3).catch(_ => never_resolve)
])
console.log(product)
})()
}

two Promise.all() on the same page

I have two Promise.all() on the same page like this:
// promiseGroup1
Promise.all([promise1, promise2, promise3]).then(function(values1) {
doStuff1(values1)
})
// promiseGroup2
Promise.all([promise4, promise5, promise6]).then(function(values2) {
doStuff2(values2)
})
I want everything to start ASAP, and to let promiseGroup1 to continue to doStuff1() if promiseGroup1 finishes first, but doStuff2() to wait doStuff1()to finish. How can I implement this?
If you want one to wait for the other, then you don't want them to be separate async Promises. Instead of trying to force them to be synchronous, why not just include all the promises in one Promise.all and then call doStuff1() and doStuff2() in sequence in the same .then callback?
Keep in mind that the individual Promises you're passing to Promise.all will begin running as soon as they're created; they won't wait to run for Promise.all. So it's not like putting them in different groups changes which resolve first.
To clarify, what you're asking for is equivalent to this:
Promise.all([promise1, promise2, promise3, promise4, promise5, promise6]).then(function(values1) {
doStuff1(values1);
doStuff2(values1.slice(3));
});
Unless doStuff1 has side effects that you want to happen before the second set of promises resolve, if the first set resolves first? That would be weird and probably deserving of refactoring, but for that, just return the second promise from the handler of the first and chain them:
Promise.all([promise1, promise2, promise3]).then(function(values) {
doStuff1(values1);
return Promise.all([promise4, promise5, promise6]);
})
.then(function(values) {
doStuff2(values);
});
Sure!
// promiseGroup1
const stuff1Promise = new Promise(resolveStuff1 => {
Promise.all([promise1, promise2, promise3]).then(function(values) {
const stuff1 = doStuff1();
resolveStuff1(stuff1);
})
});
// promiseGroup2
Promise.all([promise4, promise5, promise6, stuff1Promise]).then(function(values) {
doStuff2()
})
This lets both your promise groups to kick off simultaneously, and also ensures that your doStuff2 is invoked only after your doStuff1 has finished.

Categories