Construct object from multiple promises - javascript

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,
};

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 })));
})
);

How can I call multiple asynchronous depended functions asynchronously for each iteration?

I have some functions,
const firstResult = await firstFunction(parameters)
const secondResult = await secondFunction(firstResult)
const finalResult = await finalFunction(secondResult)
They're asynchronous functions, returning some promise. Now there's a scenario like I have an array of parameters that is getting passed to `firstFunction at each iteration and it should follow the same flow of execution. Something like this:
const results = arrayOfParamters.map(async parameter => {
const firstResult = await firstFunction(parameters)
const secondResult = await secondFunction(firstResult)
const finalResult = await finalFunction(secondResult)
return finalResult
})
So how can I achieve this more efficiently? One of the efficient solutions that I can visualize is to execute firstFunction for the first parameter, start executing secondFunction and till then the promise for secondFunction is fulfilled, start executing the firstFunction for the next iteration of arrayOfParameters. Is there any way I can achieve this? Seems like Promise.all will not be worth it if the whole loop is going to be executed synchronously.
Your code is already set up to do that.
It's going to start by running the map code for element 0 of the array. It synchronously calls firstFunction but then it hits an await, and so the mapping function returns a promise. This promise gets put in element 0 of results. Then it does the same thing for element 1, then 2, etc. So it will call firstFunction for every element of the array, and assign an array of promises to results, but none of them will block or wait.
The array of promises has now been created, so the code continues on to whatever comes after this line. Over time, the calls to firstFunction will start finishing, and the async functions will resume, calling secondFunction for each element of the array (not necessarily in exactly the same order, since this is happening at an unpredictable time). And then eventually they'll all do third function, and finally they'll resolve their promises to finalResult.
So the only thing missing from your code is to know when it's all done, and for that you need Promise.all:
const promises = arrayOfParamters.map(async parameter => {
const firstResult = await firstFunction(parameter)
const secondResult = await secondFunction(firstResult)
const finalResult = await finalFunction(secondResult)
return finalResult
})
const results = await Promise.all(promises)
Because Promise.then chain the result as a parameter to the next function you can use that instead of async await
You can use Promise.all like this:
const promiseChain = arrayOfParamters.map((parameter) => firstFunction(parameter).then(secondFunction).then(finalFunction));
const results = await Promise.all(promiseChain);

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);
}

Difference between Serial and parallel

I am fetching some persons from an API and then executing them in parallel, how would the code look if i did it in serial?
Not even sure, if below is in parallel, i'm having a hard time figuring out the difference between the two of them.
I guess serial is one by one, and parallel (promise.all) is waiting for all promises to be resolved before it puts the value in finalResult?
Is this understood correct?
Below is a snippet of my code.
Thanks in advance.
const fetch = require('node-fetch')
const URL = "https://swapi.co/api/people/";
async function fetchPerson(url){
const result = await fetch(url);
const data = await result.json().then((data)=>data);
return data ;
}
async function printNames() {
const person1 = await fetchPerson(URL+1);
const person2 = await fetchPerson(URL+2);
let finalResult =await Promise.all([person1.name, person2.name]);
console.log(finalResult);
}
printNames().catch((e)=>{
console.log('There was an error :', e)
});
Let me translate this code into a few different versions. First, the original version, but with extra work removed:
const fetch = require('node-fetch')
const URL = "https://swapi.co/api/people/";
async function fetchPerson(url){
const result = await fetch(url);
return await result.json();
}
async function printNames() {
const person1 = await fetchPerson(URL+1);
const person2 = await fetchPerson(URL+2);
console.log([person1.name, person2.name]);
}
try {
await printNames();
} catch(error) {
console.error(error);
}
The code above is equivalent to the original code you posted. Now to get a better understanding of what's going on here, let's translate this to the exact same code pre-async/await.
const fetch = require('node-fetch')
const URL = "https://swapi.co/api/people/";
function fetchPerson(url){
return fetch(url).then((result) => {
return result.json();
});
}
function printNames() {
let results = [];
return fetchPerson(URL+1).then((person1) => {
results.push(person1.name);
return fetchPerson(URL+2);
}).then((person2) => {
results.push(person2.name);
console.log(results);
});
}
printNames().catch((error) => {
console.error(error);
});
The code above is equivalent to the original code you posted, I just did the extra work the JS translator will do. I feel like this makes it a little more clear. Following the code above, we will do the following:
Call printNames()
printNames() will request person1 and wait for the response.
printNames() will request person2 and wait for the response.
printNames() will print the results
As you can imagine, this can be improved. Let's request both persons at the same time. We can do that with the following code
const fetch = require('node-fetch')
const URL = "https://swapi.co/api/people/";
function fetchPerson(url){
return fetch(url).then((result) => {
return result.json();
});
}
function printNames() {
return Promise.all([fetchPerson(URL+1).then((person1) => person1.name), fetchPerson(URL+2).then((person2) => person2.name)]).then((results) => {
console.log(results);
});
}
printNames().catch((error) => {
console.error(error);
});
This code is NOT equivalent to the original code posted. Instead of performing everything serially, we are now fetching the different users in parallel. Now our code does the following
Call printNames()
printNames() will
Send a request for person1
Send a request for person2
printNames() will wait for a response from both of the requests
printNames() will print the results
The moral of the story is that async/await is not a replacement for Promises in all situations, it is syntactic sugar to make a very specific way of handling Promises easier. If you want/can perform tasks in parallel, don't use async/await on each individual Promise. You can instead do something like the following:
const fetch = require('node-fetch')
const URL = "https://swapi.co/api/people/";
async function fetchPerson(url){
const result = await fetch(url);
return result.json();
}
async function printNames() {
const [ person1, person2 ] = await Promise.all([
fetchPerson(URL+1),
fetchPerson(URL+2),
]);
console.log([person1.name, person2.name]);
}
printNames().catch((error) => {
console.error(error);
});
Disclaimer
printNames() (and everything else) doesn't really wait for anything. It will continue executing any and all code that comes after the I/O that does not appear in a callback for the I/O. await simply creates a callback of the remaining code that is called when the I/O has finished. For example, the following code snippet will not do what you expect.
const results = Promise.all([promise1, promise2]);
console.log(results); // Gets executed immediately, so the Promise returned by Promise.all() is printed, not the results.
Serial vs. Parallel
In regards to my discussion with the OP in the comments, I wanted to also add a description of serial vs. parallel work. I'm not sure how familiar you are with the different concepts, so I'll give a pretty abstraction description of them.
First, I find it prudent to say that JS does not support parallel operations within the same JS environment. Specifically, unlike other languages where I can spin up a thread to perform any work in parallel, JS can only (appear to) perform work in parallel if something else is doing the work (I/O).
That being said, let's start off with a simple description of what serial vs. parallel looks like. Imagine, if you will, doing homework for 4 different classes. The time it takes to do each class's homework can be seen in the table below.
Class | Time
1 | 5
2 | 10
3 | 15
4 | 2
Naturally, the work you do will happen serially and look something like
You_Instance1: doClass1Homework() -> doClass2Homework() -> doClass3Homework() -> doClass4Homework()
Doing the homework serially would take 32 units of time. However, wouldn't be great if you could split yourself into 4 different instances of yourself? If that were the case, you could have an instance of yourself for each of your classes. This might look something like
You_Instance1: doClass1Homework()
You_Instance2: doClass2Homework()
You_Instance3: doClass3Homework()
You_Instance4: doClass4Homework()
Working in parallel, you can now finish your homework in 15 units of time! That's less than half the time.
"But wait," you say, "there has to be some disadvantage to splitting myself into multiple instances to do my homework or everybody would be doing it."
You are correct. There is some overhead to splitting yourself into multiple instances. Let's say that splitting yourself requires deep meditation and an out of body experience, which takes 5 units of time. Now finishing your homework would look something like:
You_Instance1: split() -> split() -> doClass1Homework()
You_Instance2: split() -> doClass2Homework()
You_Instance3: doClass3Homework()
You_Instance4: doClass4Homework()
Now instead of taking 15 units of time, completing your homework takes 25 units of time. This is still cheaper than doing all of your homework by yourself.
Summary (Skip here if you understand serial vs. parallel execution)
This may be a silly example, but this is exactly what serial vs. parallel execution looks like. The main advantage of parallel execution is that you can perform several long running tasks at the same time. Since multiple workers are doing something at the same time, the work gets done faster.
However, there are disadvantages. Two of the big ones are overhead and complexity. Executing code in parallel isn't free, no matter what environment/language you use. In JS, parallel execution can be quite expensive because this is achieved by sending a request to a server. Depending on various factors, the round trip can take 10s to 100s of milliseconds. That is extremely slow for modern computers. This is why parallel execution is usually reserved for long running processes (completing homework) or when it just cannot be avoided (loading data from disk or a server).
The other main disadvantage is the added complexity. Coordinating multiple tasks occurring in parallel can be difficult (Dining Philosophers, Consumer-Producer Problem, Starvation, Race Conditions). But in JS the complexity also comes from understanding the code (Callback Hell, understanding what gets executed when). As mentioned above, a set of instructions occurring after asynchronous code does not wait to execute until the asynchronous code completes (unless it occurs in a callback).
How could I get multiple instances of myself to do my homework in JS?
There are a couple of different ways to accomplish this. One way you could do this is by setting up 4 different servers. Let's call them class1Server, class2Server, class3Server, and class4Server. Now to make these servers do your homework in parallel, you would do something like this:
Promise.all([
startServer1(),
startServer2(),
startServer3(),
startServer4()
]).then(() => {
console.log("Homework done!");
}).catch(() => {
console.error("One of me had trouble completing my homework :(");
});
Promise.all() returns a Promise that either resolves when all of the Promises are resolved or rejects when one of them is rejected.
Both functions fetchPerson and printNames run in serial as you are awaiting the results. The Promise.all use is pointless in your case, since the both persons already have been awaited (resolved).
To fetch two persons in parallel:
const [p1, p2] = await Promise.all([fetchPerson(URL + '1'), fetchPerson(URL + '2')])
Given two async functions:
const foo = async () => {...}
const bar = async () => {...}
This is serial:
const x = await foo()
const y = await bar()
This is parallel:
const [x,y] = await Promise.all([foo(), bar()])

When using the await?

I'm using sequelize with typescript. I know that the code is asynchronous, and
Here, I am using promise, and the code works..
I would want to know when I must to use await keyword ?
const promises = []
let tabIdDoc = requestedListIdDoc.toString().split(",")
for (let thisIdDoc of tabIdDoc) {
promises.push(sequelize.models['Document'].findById(thisIdDoc))
}
q.all(promises).then((resultReq) => {
const lstDocs = []
for (let MyDoc of resultReq) {
if (MyDoc.matriculeDoc != "") {
lstDocs.push(sequelize.models['FolderDocContent'].findOrCreate({
where: {
}
}))
}
}
q.all(lstDocs).then(() => {
return response.status(201)
})
}
Is await keyword necessary here ?
You don't ever have to use await as other programming using .then() can always get the job done, but there are numerous times when using await makes your code simpler. This is particularly true when you are trying to sequence a number of asynchronous operations and even more so when you need to use previous results in more than one operation that follows.
Example #1: Serializing operations in a for loop
Suppose you want to save a bunch of items to your database, but for various reasons, you need to save them one by one and you need to save them in order (in other words, you need to sequence them):
async function saveItems(shoppingList) {
for (let item of shoppingList) {
// for loop will pause until this promise resolves
await db.save(item);
}
}
saveItems(myList).then(() => {
// all done
}).catch(err => {
// error here
});
Without using await, you'd have to use a significantly more verbose design pattern using .reduce() or perhaps a recursive function that you call on the completion of the prior operation. This is a lot simpler way to sequence iteration of a loop.
Example #2: Sequencing different operations in a function
Suppose, you need to contact three different outside services. You need to get some data from one, then use that data when you make a second API call, then use both of those pieces of data in a third API call:
const rp = require('request-promise');
async function getPrice(productName) {
// look up productID
const productID = await rp(`http://service1.com/api/getID?name=${productName}`);
// use productID to lookup price
const productPrice = await rp(`http://service1.com/api/getPrice?id=${productID}`);
// put both productID and price into the shopping cart
return rp({uri: 'http://service1.com/api/addToCart', body: {name: productName, id: productID}, json: true);
}
getPrice("Nikon_d750").then(total => {
// all done here, total is new cart total
}).catch(err => {
// error here
});
Both of these examples would require more code in order to properly sequence the asynchronous operations and you'd have to nest logic following inside a .then() handler. Using await instructs the JS interpreter to do that nesting for you automatically.
Some other examples here on MDN.
Several rules to keep in mind with await:
You can only use it inside a function prefixed with the async keyword as in my examples above.
All async functions return a promise. If you have an explicit return value in the function as in return x, then that value becomes the resolved value of the promise when all the asynchronous operations are done. Otherwise, it just returns a promise that has an undefined resolved value. So, to use a result from an async function or to know when it's done, you either have to await the result of that function (inside another async function) or you have to use .then() on it.
await only suspends execution within the containing function. It is as if the rest of the code in the function is placed inside an invisible .then() handler and the function will still return its promise immediately when it hits the first await. It does not block the event loop or block other execution outside the async function. This confuses many people when they first encounter await.
If the promise that you await rejects, then it throws an exception within your function. That exception can be caught with try/catch. If it is not caught, then the async function automatically catches it and rejects the promise that the function returns where the thrown value is the reject reason. It is easy when first using await to completely forget about the error cases when promises you are awaiting can reject.
You never necessarily must use await. You however should use await in every place where you'd otherwise have then with a callback, to simplify your code.
In your example:
const promises = requestedListIdDoc.toString().split(",").map(thisIdDoc =>
sequelize.models['Document'].findById(thisIdDoc)
);
const resultReq = await q.all(promises);
const lstDocs = resultReq.filter(myDoc => MyDoc.matriculeDoc != "").map(myDoc =>
sequelize.models['FolderDocContent'].findOrCreate({
where: {}
})
);
await q.all(lstDocs);
return response.status(201)

Categories

Resources