Nested promise.all - javascript

I am try to improve some performance problem and for this I am try to do followings
I have a promises array and I wanna return result of this after all promises at promises array are done.
by the way result of processA, processB and processC are important too because at // other logic I use them at somewhere.
const promiseResults = await Promise.all(promises);
const [processA, processB, processC] = await Promise.all([
asyncProcessForA(),
asyncProcessForB(),
asyncProcessForC(),
]);
// other logic
// ....
// ....
return promiseResults;
So, I wanna add promiseResults inside promise.all like
const [promiseResults, processA, processB, processC] = await Promise.all([
Promise.all(promises),
asyncProcessForA(),
asyncProcessForB(),
asyncProcessForC(),
]);
// other logic
// ....
// ....
return promiseResults;
So, Is my way is good or not and also using nested promise.all is a good way or not? Because I do some search for best practices for using nested promise.all but I could not find anything So, what do you offer ?

Using a nested Promise.all() as shown in your second example:
const [promiseResults, processA, processB, processC] = await Promise.all([
Promise.all(promises),
asyncProcessForA(),
asyncProcessForB(),
asyncProcessForC(),
]);
Works just fine. Promise.all() expects an array of promises. Since Promise.all() returns a promise, it's perfectly fine to use its promise in another call to Promise.all(). The one gotcha here is that your outer Promise.all() will resolve to an array that has an embedded array in it like this:
[[v1, v2, v3], valueA, valueB, valueC]
Where [v1, v2, v3] are the resolved results from the inner Promise.all() (as passed through to the outer Promise.all()andvalueA, valueB, valueCare the resolved results from the other promises passed to the outerPromise.all()`. So, as long as you're prepared for the results to be organized like this, you can certainly work it this way.
You could also flatten the results and do something like this:
const [processA, processB, processC, ...promiseResults] = await Promise.all([
asyncProcessForA(),
asyncProcessForB(),
asyncProcessForC(),
...promiseResults
]);
This would then give you a single combined and flat array of results which you can then assign out of or access however you want. So, it really just depends upon how you want the results organized.

I haven’t use such construction, but if you don’t like it, you can wrap your promise.all in another promise, so it won’t be nested promise all.

Related

How to resolve nested promises with Promise.all()

I am trying to write something like this but not able to resolve inner promises.
const traceSamplesRequests = transactionTypes.map(async (transactionType) => {
// This part should be synchronous since we need transactionNames
const transactionNames = await api.getTransactionNames();
return transactionNames.map((transactionName) => api.getTraceSamples());
});
And I want to write with lambda functions with Promise.all not imperative loops.
So, .map() is not promise-aware. It calls the callback and just dutifully collects the return value from the callback and advances to the next iteration of the loop. Since your callback is async with an await in it, that return value is an unresolved promise. So, what you get back from .map() is an array of promises.
So, there is no way to use .map() or any of the array iteration methods without then using something to wait for the promises.
It sounds like you already know this, but with the code you have, you would insert two Promise.all() statements:
const traceSamplesRequests = await Promise.all(transactionTypes.map(async (transactionType) => {
// This part should be synchronous since we need transactionNames
const transactionNames = await api.getTransactionNames();
return Promise.all(transactionNames.map((transactionName) => api.getTraceSamples()));
}));
That would get you an array of values (and the parent function would have to be async). This will run all the asynchronous operation in the loop in parallel (all in flight at the same time).
Your other choices are to use a for or while loop which will suspend loop iteration for each await and will run your asynchronous operations in sequence (second iteration of the loop not running until the first is done).
Or, you can write some sort of helper function that would be a promise-aware version of .map(). You'd probably just either use for or .map() with Promise.all() inside the helper function though.
The Async promise library has multiple such helper functions as choices to use. They are designed to work with either promises or plain callbacks.
I actually made it work like this:
const traceSamplesRequests = await Promise.all(
transactionTypes.map(async (transactionType) => {
const transactionNames = await api.getTransactionNames({ transactionType });
return await Promise.all(transactionNames.map((transactionName) => api.getTraceSamples({ transactionType })));
})
);

Promise.all storing to a variable from first promise to use in the second promise [duplicate]

I've been looked on StackOverflow and haven't seen any direct examples of what I'm asking. I'm reading this article on memoization link here if you want to look.
It appears to me that you should be able to run them all together and use the return value from getSoupRecipe() as input for hireSoupChef()
async function makeSoupFromType(soupType) {
let [ soupRecipe, soupPan, soupChef ] = await Promise.all([
getSoupRecipe(soupType),
buySoupPan(),
hireSoupChef(soupRecipe.requiredSkills)
]);
return await makeSoup(soupChef, soupRecipe, soupPan);
}
So the question is can all three async functions run at the same time and once getSoupRecipe returns I use it's variable name (which is soupRecipe) as input for hireSoupChef.
I would post all the other code here for you to see but I think it would probably make the question look too daunting, so the link is above. You don't necessarily have to look at it to understand I don't think because I think I've stated the question right, but nonetheless if you want to you can.
Not by itself, no. In your example the soupRecipe (and the other two variables) are only initialised after the Promise.all(…) has been awaited, so it can't be used in the expressions that are passed to Promise.all as an argument. There's no magic going on here, Promise.all is not part of the syntax but really just returning one promise that fulfills with an array once.
However, the approach outlined in this answer to How do I access previous promise results in a .then() chain? does enable the desired chaining of asynchronous actions:
async function makeSoupFromType(soupType) {
const soupRecipePromise = getSoupRecipe(soupType);
const [soupRecipe, soupPan, soupChef] = await Promise.all([
soupRecipePromise,
buySoupPan(),
soupRecipePromise.then(({requiredSkills}) => hireSoupChef(requiredSkills))
]);
return makeSoup(soupChef, soupRecipe, soupPan);
}
Alternatively, with plain async/await you could also use an IIFE to avoid the then() call:
async function makeSoupFromType(soupType) {
const [soupPan, [soupRecipe, soupChef]] = await Promise.all([
buySoupPan(),
(async () => {
const soupRecipe = await getSoupRecipe(soupType);
const soupChef = await hireSoupChef(soupRecipe.requiredSkills);
return [soupRecipe, soupChef];
})(),
]);
return makeSoup(soupChef, soupRecipe, soupPan);
}

Is the promise resolved, when I push promise array?

I want to push promise to an array. Then I want to resolve it using Promise.all(). But I'm not sure, is promise work when i push to array?
For example:
const productIds= [1,2,3,4,5,6] // example
const promises = [];
productIds.map(productId => promises.push(ProductDataAccess.updateProductStock(store,productId,incQuery)));
If I don't Promise.all(), will db process occur? Or does the db process occur when I make Promise.all().
const productIds= [1,2,3,4,5,6] // example
const promises = [];
productIds.map(productId => promises.push(ProductDataAccess.updateProductStock(store,productId,incQuery)));
Pormise.all(promises);
If I don't Promise.all(), will db process occur?
It depends. The general answer is yes. But be aware that this is not always true.
Normally, functions that return a Promise schedules an asynchronous process when you call them. So the asynchronous process will happen regardless of you waiting for them.
However, there are some functions that don't really return a promise. They return a promise-like object. And sometimes (not always) they don't start the asynchronous process unless you call .then(). And database libraries that use fluent/chainable functions do this. This is a common design pattern in database libraries:
// knex example:
let x = knex('my_table').select(['id','some_info']); // will not trigger db query
console.log(x); // knex object - not a Promise!
x = x.where('id', 0); // still no db query
console.log(x); // still not a Promise!
x = x.then(result => console.log(result)); // TRIGGERS DB QUERY!
console.log(x); // Yay! A Promise!
Lots of database libraries do this in order to implement a fluent/chaining style API that is transparent to the user. They detect the end of query construction by the .then() function being called.
Now, I don't know what database library you are using but if you are affected by this then to trigger the database query process you will need to call then either directly or indirectly:
call .then() yourself
pass the object to Promise.all() which internally calls .then()
await the result in an async function which internally calls .then()
Yes, promises run when they are created, not when they are awaited upon (be it with Promise.all() or otherwise).
You will start the execution of the promises from the moment each promise is created (regardless of the usage of Promise.all), but not wait for them.
If this block of code is in an async function, you can await the promises in this way (and also, you can simply use map, without push, to build the array of promises you need):
const productIds = [1,2,3,4,5,6];
const promises = productIds.map((productId) => ProductDataAccess.updateProductStock(store,productId,incQuery));
await Promise.all(promises);

Construct object from multiple promises

I want to construct an object from multiple promise results. The only way to do this is something like this:
const allPromises = await Promise.all(asyncResult1, asyncResult2);
allPromises.then([result1, result2] => {
return {result1, result2};
})
A nicer way (especially when more data is being constructed) would be this, with the major drawback of the async requests happening one after the other.
const result = {
result1: await asyncResult1(),
result2: await asyncResult2(),
}
Is there a better way to construct my object with async data I am waiting for?
See my comment, it's a little unclear what asyncResult and asyncResult2 are, because your first code block uses them as though they were variables containing promises (presumably for things already in progress), but your second code block calls them as functions and uses the result (presumably a promise).
If they're variables containing promises
...for processes already underway, then there's no problem with awaiting them individually, since they're already underway:
const result = {
result1: await asyncResult1, // Note these aren't function calls
result2: await asyncResult2,
};
That won't make the second wait on the first, because they're both presumably already underway before this part of the code is reached.
If they're functions
...that you need to call, then yes, to call them both and use the results Promise.all is a good choice. Here's a tweak:
const result = await Promise.all([asyncResult1(), asyncResult2()])
.then(([result1, result2]) => ({result1, result2}));
One of the rare places where using then syntax in an async function isn't necessarily the wrong choice. :-) Alternately, just save the promises to variables and then use the first option above:
const ar1 = asyncResult1();
const ar2 = asyncResult2();
const result = {
result1: await ar1,
result2: await ar2,
};

Does Promise.all() execute an array of functions or do they execute when you put them into the array?

Since await does not work inside Array.map or Array.reduce, can you do something like the following or would this be considered misuse of Promise.all? Normally, neo4j.session() would be awaited.
// inside a function
const QUERY = 'MATCH (n) RETURN n'
const argsArray = [{ sample: 'sadf' }, { sample: 'sadf' }, { sample: 'sadf' }]
const runQueries = argsArray.map(obj => neo4j.session.run(QUERY, obj.sample))
await Promise.all(runQueries)
.then(results => results.forEach(result => console.log(result)))
Does Promise.all() execute an array of functions?
No its an array of promises
or do they execute when you put them into the array?
Exactly, When you build the Promises they're executed.
would this be considered misuse of Promise.all?
No this is totally fine, its actually the point of Promise.all.
However you might do (one after another instead of parallel execution) :
(async function(){
for(const obj of argsArray)
console.log( await neo4j.session.run(QUERY, obj.sample));
})()
async.await is supposed to be syntactic sugar for sequential promise chains. Considering that database queries are supposed to run concurrently, it's perfectly fine to use await Promise.all(...) in such cases.
Promise.all accepts an array of promises (more specifically, an iterable), and promises start to execute at the moment when they are created. It may be the moment prior to Promise.all call:
const promises = [Promise.resolve(1), Promise.resolve(2)];
// promises have been created at this point
Promise.all(promises).then(...)
Or it may be not. If an iterable is not an array but a generator, promises will be lazily created during Promise.all call:
const promiseGen = (function* () {
yield Promise.resolve(1);
yield Promise.resolve(2);
})();
// promises have not been created yet at this point
Promise.all(promiseGen).then(...)
// promises have been created

Categories

Resources