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.
Related
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);
})();
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.
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(...).
I am adding a set of instructions to be performed by Promise.all() at the end of my code.
What I have noticed is that the .remove() instructions are executed even if Promise.all() is NOT called. (??)
My code:
const myObject = { test:1 }
let myPromises = []
// This .set() will not get executed since I am not calling Promise.all()
console.log(`dbRoot.child('${path}/${uid}/${id}').set(myObject)`)
myPromises.push(dbRoot.child(`${path}/${uid}/${id}`).set(myObject))
// This .remove() gets executed at once, even though Promise.all is never called
console.log(`dbRoot.child('${path}/${uid}/${id}').remove()`)
myPromises.push(dbRoot.child(`${path}/${uid}/${id}`).remove())
// These lines have been excluded to prevent promises from being carried out
/*
return Promise.all(myPromises)
.then(result => Promise.resolve(true))
.catch((err)=>{
console.error('error', err)
return Promise.reject(err)
})
*/
I have checked my code and .set(null) or .remove() is not called from anywhere else but these lines.
How can I collect all .set() and .remove() and carry them all out at once synchronously? (Maybe using Promise.all ?)
Kind regards /K
If you don't want a promise to execute immediately, simply wrap it in a function. You can then execute each function only when you call Promise.all:
const actions = [
() => dbRoot.child(`${path}/${uid}/${id}`).set(myObject),
() => dbRoot.child(`${path}/${uid}/${id}`).remove(),
]
;(async () => {
const results = await Promise.all(actions.map(fn => fn()))
console.log(results)
})()
Note that Promise.all executes promises in parallel. If you instead want them executed in series, use Array#reduce or for await ... of instead.
To quote the documentation for both .set and .remove
The effect of the write will be visible immediately, ... the returned Promise will resolve when complete. If provided, the onComplete callback will be called asynchronously after synchronization has finished.
i.e. As soon as you call the .set or .remove method, firebase will attempt to perform the write action.
Re: Using Promise.all
Do you mean this is an incorrect approach when calling Set/Remove?
No, I'm not saying this is an incorrect approach. What I'm saying is that the write action happens immediately, regardless of whether or not you use Promise.all.
All that Promise.all does, is ensure that every promise in the provided array has been resolved.
How can I collect all .set() and .remove() and carry them all out at once synchronously?
Promise.all
Sources:
Set, Remove
If I have an array of elements and I want to do parallel operations on them.
I would use promise.all().
I knew promise.all() accepts array of promises. Correct me if I am wrong, I don't think so.
Here, it clearly says.
The Promise.all() method returns a single Promise that fulfills when all of the promises passed as an iterable have been fulfilled or when the iterable contains no promises or when the iterable contains promises that have been fulfilled and non-promises that have been returned. It rejects with the reason of the first promise that rejects, or with the error caught by the first argument if that argument has caught an error inside it using try/catch/throw blocks.
So, yes we can pass simple functions to promise.all(), and it resolves if they return and rejects if they throw an error.
Now look at the below code.
const promises = todayAssignedJobs.map(async todayAssigned => {
const [leaderboard, created] = await Leaderboard.findOrCreate({...});
if (!created) {
const rating = (todayAssigned.rating + leaderboard.rating * leaderboard.jobs_completed) / (leaderboard.jobs_completed + 1);
const commission = todayAssigned.commission + leaderboard.commission;
const jobsCompleted = leaderboard.jobs_completed + 1;
await Leaderboard.update({
rating,
commission,
jobs_completed: jobsCompleted,
updated_by: 'system',
}, {
where: {
id: leaderboard.id,
},
});
}
await AssignedJob.update({
is_leaderboard_generated: true,
}, {
where: {
id: todayAssigned.id,
},
});
});
await Promise.all(promises);
Here, I have a doubt.
We are iterating on each element of the array and doing operation on them asyncrhonously. They don't return anything explicitly.
So, I think map is doing parallel operations here too.
Why shuold then use promise.all() here?
.map() is not promise-aware. So, when you pass it an async callback like you are, it does not pay any attention to that returned promise. So, it just runs the loop one after another, not waiting for any of the returning promises. Thus, all the asynchronous operations started in the .map() loop will be in-flight at the same time.
If that's what you want and you want to collect all the returned promises so you can later see when they are all done with Promise.all(), then this pattern works well:
Promise.all(someArray.map(callbackThatReturnsAPromiseHere))
And, this is a common design pattern for it. In fact, the Bluebird promise library has a special function that combined these two called Promise.map(). It also offered another nice feature that let you control how many concurrent async operations could run at once (because it's map() operation is promise-aware).
It sounds like you're trying to figure out if you should only use .map() and not use Promise.all(). If you do that, you will run your asynchronous operations in parallel, but you will not have any idea when they are all done or any ability to collect all the results. You would use Promise.all() on the array of returned promises to know when they are all done and/or to collect their resolved results.
FYI, .map() is JUST a plain loop. It doesn't have any special asynchronous features or any special run-in-parallel features. You can do the same thing with a for loop if you want. It does NOT pause for your async callback to wait for it to be done so a side effect of running it is that you launch a bunch of parallel asynchronous operations.
The purpose of Promise.all here is to be able to await all the promises before continuing.
If you added a console.log immediately before the await Promise.all(promises); it would run synchronously before any promises have resolved, whereas a console.log immediately after that line will only appear after all the promises have resolved.
You only need Promise.all if you want to do something when all operations are complete. For example:
const promises = todayAssignedJobs.map(async todayAssigned => {
// lots of async stuff
});
await Promise.all(promises);
// now, all Promises have resolved
// alert the user that the leaderboard is completely updated
If you don't need anything to occur once you've determined that all Promises have completed, then there's no point to the Promise.all - you may as well just create the Promises in a loop and leave them as-is. In that case, since you wouldn't be using the resulting array of Promises, it would be more appropriate to use forEach (which is the array method to use for side-effects).
There's one issue, though - you aren't handling errors, but errors should be handled, otherwise they'll give warnings or exit the Node process:
const processJob = async (todayAssigned) => {
const [leaderboard, created] = await Leaderboard.findOrCreate({...});
if (!created) {
// ...
// ...
todayAssignedJobs.forEach(async todayAssigned => {
try {
await processJob(todayAssigned);
} catch(e) {
// handle errors
}
});