I'm trying to work with data sent from postman, array of objects.
I need to write all data into database, so I use mapping of array, and get undefined
const providers = await req.body.providers.map((provider) => {
ProviderService.createProvider(provider.name, container._id);
});
Promise.all(providers).then((value) => {
console.log(value);
});
I get from console log array of undefined, but items are creating in Data Base, I know I have mistake in asynchronous functions, but I don't really understand - where
Thank you for answer
if I got this right, when you use the await keyword inside async function you actually wait for the promise to be resolved then you assign its extracted value to the providers variable.
Promise.all takes as parameter an array of promises, so I guess this is where things go wrong when you send it an array with values.
Your first statement is returning an Array. You can only use await on promises.
Promise.all() does return a promise though.
So you just need to move the await to the correct place.
const providers = req.body.providers.map((provider) => {
ProviderService.createProvider(provider.name, container._id);
});
await Promise.all(providers).then((value) => {
console.log(value);
});
Related
I have the state array "days" and I have a function that gets the element with an id given by the argumet, i am using the array.filter method to do this. Assuming the array has a lot of data in it, do i need to make the function asynchronous to wait for the filter method to print that data?
const [days, setDays] = useState([]);
const onDayClicked = async (id) => {
const daySelected = await days.filter(el => el._id === id)
console.log(daySelected)
}
filter returns:
A new array with the elements that pass the test. If no elements pass the test, an empty array will be returned.
An array is not a promise.
await is only useful if you have a promise on the RHS.
If you have an expensive, blocking operation to do, then it is simply an expensive operation that will block the event loop. You can't get around that by throwing promise syntax at it.
You could get around it by moving the work out of the main event loop and into a web worker.
If you did that, then you could create a promise that resolves when the web worker sends the results back to the main event loop.
And if you did that, then you could use await so that daySelected would have the resolved value in it.
I have an array of objects that I get from database.
const users = await User.findAll();
To each of them, I want to call an async function user.getDependency() to get another related table, and devolve it nested in the object to a final format like this:
[
User {
attr1: value,
attr2: value,
...
Dependency: {
attr1: value,
...
}
},
User {
...
and so on
Now, I get my problem. The only possibilities I am being able to think involving async and loops are 2:
Option 1: mapping them and getting the promise back:
users.forEach((user) => {
user.dependency= user.getDependency();
});
With this I get similar to the desired outcome in terms of format, only that instead of the resolved thing i get obviously Dependency: Promise { <pending> }, that is, the promises all nested inside each user object of the array. And I don't know how to procceed with it. How to loop through all the user objects and resolve this promises they have inside?
Option 2: return the promises to be resolved in Promise.all
const dependencies = Promise.all(users.map((user) => {
return user.getDependency();
}));
This give me a separated array with all the dependecies I want, but that's it, separated. I get one array with the objects users and another with the objects dependencies.
What is bugging me specially is that I am thinking that there must be a simple straightforward way to do it and I am missing. Anyone have an idea? Much thanks
To build some intuition, here is your forEach example converted with a push in the right direction.
users.forEach((user) => {
user.getDependency().then(dependency => {
user.dependency = dependency
})
})
The above reads "for each user, start off the dependency fetching, then add the dependency to the user once resolved." I don't imagine the above is very useful, however, as you would probably like to be able to do something with the users once all the dependencies have finished fetching. In such a case like that, you could use Promise.all along side .map
const usersWithResolvedDependencies = await Promise.all(
users.map(user => user.getDependency().then(dependency => {
user.dependency = dependency
return user
}))
)
// do stuff with users
I should mention I wrote a library that simplifies async, and could certainly work to simplify this issue as well. The following is equivalent to the above. map and assign are from my library, rubico
const usersWithResolvedDependencies = map(assign({
dependency: user => user.getDependency()
}))(users)
I guess the best way is to do like this. Mapping users, updating every object (better to do this without mutation), and using Promise.all
const pupulatedUsers = await Promise.all(users.map(async (user) => ({
...user,
dependency: await getDependency(user.dependency_id)
// or something like getDependency
// function that fires async operation to get needed data
}));
you're right there is a straightforward way to resolve the promise
as the users variable is not a promise waiting to be resolved, you should try
users.then((userList) => {
// function logic here
})
the userList in the then function should be iterable and contain the desired user list
also you can also handle the rejected part of the promise, if at all the query fails and the promise returns an error:
users.then((error) => {
// error handling
})
you should give this document a glance to get more info about handling promises to further know what goes on in that query when it returns a promise
I have been struggling with this issue for a week and have researched myself close to death. I am a total newbie. I have managed to grasp the crux of promises, but I am failing to see how to include this in a loop.
I have an app that is looking through an array. There is some validation of the array against a mongoose database (which is taking time to run). I am trying to push items into a new array based on some of this validation. I know the validation is working because of the console log in the loop. However my final array is not waiting for the loop to finish. Which means I need to put the loop into a promise, or well I think, but the issue is that I don't know how to resolve it. Current output is a blank array instead of the validated array. Here is my code:
//dummy data of an array - this is originally extracted from a mongoose DB and works (it's my first promise).
const appArray = ["5f8f25d554f1e43f3089ea5d",
"5f8f25e854f1e43f3089ea5e",
"5f8f25f454f1e43f3089ea5f",
"5f8f314ab92c7f406f28b83a",
"5f8fe50a9d44694cad91a01b",
"5f92e8a75d848870e015dff3",
"5f92e8b35d848870e015dff4",
"5f92e8cb5d848870e015dff5",
"5f8fe51d9d44694cad91a01c"];
//the second promise takes the array above and validates it against another collection on mongoose
function myPromise2 (response){
return new Promise((resolve, reject) => {
let appoints = [];
response.forEach(e => {
//loop through each item of the array and look against Appointment collection
Appointment.findById(e, function(err, foundApp){
//some validation supposed to happen here and then pushed into a new array
appoints.push(foundApp);
console.log(appoints);
})
})
//once completed supposed to resolve and return
resolve(appoints);
})
};
myPromise2(appArray).then((response) => {console.log(response)});
Here is an example which should work. Add a promise for each element to the array and then resolve the outer function if all promises have resolved.
// dummy data of an array - this is originally extracted from a mongoose DB and works (it's my first promise).
const appArray = ["5f8f25d554f1e43f3089ea5d",
"5f8f25e854f1e43f3089ea5e",
"5f8f25f454f1e43f3089ea5f",
"5f8f314ab92c7f406f28b83a",
"5f8fe50a9d44694cad91a01b",
"5f92e8a75d848870e015dff3",
"5f92e8b35d848870e015dff4",
"5f92e8cb5d848870e015dff5",
"5f8fe51d9d44694cad91a01c"];
// the second promise takes the array above and validates it against another collection on mongoose
function myPromise2 (response) {
let appoints = [];
response.forEach(e => {
appoints.push(new Promise((resolve) => {
//loop through each item of the array and look against Appointment collection
Appointment.findById(e, function(err, foundApp) {
//some validation supposed to happen here and then pushed into a new array
resolve(foundApp);
})
}))
})
return Promise.all(appoints)
};
myPromise2(appArray).then((response) => {console.log(response)});
Points to address:
Use the promise that mongoose provides through the .exec() method. This way you don't need new Promise
Collect these individual promises in an array (use .map instead of .forEach), and pass this array to Promise.all
If you do this, the code for myPromise2 reduces to the following:
function myPromise2 (response){
return Promise.all(response.map(e => Appointment.findById(e).exec()));
}
here is my suggestion:
const appArray = [
"5f8f25d554f1e43f3089ea5d",
"5f8f25e854f1e43f3089ea5e",
"5f8f25f454f1e43f3089ea5f",
"5f8f314ab92c7f406f28b83a",
"5f8fe50a9d44694cad91a01b",
"5f92e8a75d848870e015dff3",
"5f92e8b35d848870e015dff4",
"5f92e8cb5d848870e015dff5",
"5f8fe51d9d44694cad91a01c"
];
function myPromise2 (response){
return Promise.all(response.map(id) => {
return Appointment.findById(id).exec();
})
};
myPromise2(appArray)
.then(console.log) // response array
.catch(// handle errors)
// you can also async/await the calling part
you can also use one of:
Promise.allSettled
Prmoise.any (es2021)
Promise.race
it's just depends on how you would like to handle the responses/failures.
A good alternative to consider maybe Async/Await and have a look Async_await. This will hopefully answer all your issues
It's probably a good idea to look into how the JS event loop system works guide here,
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.
I'm trying to export an array of data objects for later use, though I can use promises to wait till all data has been added to the array before logging, I noticed when I went to use that data I couldn't because even with Promise.all, the length of the array was still zero as if nothing had changed.
I tried having the console log each time the doc.data() was pushed to the exportArray and I noticed that it logs that after it outputs the array. So for example...
Expected Output
doc.data() // For Each doc
Array[] // Filled with data and length 54
Length: 54
Actual Output
Array[] // Filled with data and length 54
Length: 0
doc.data() // For Each doc
let exportArray = [];
let promises = [];
db.collection('lists').doc('List 1').collection("members")
.get().then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
promises.push(
new Promise(function (resolve, reject) {
exportArray.push(doc.data());
resolve();
console.log('before');
})
);
});
});
Promise.all(promises).then(function () {
console.log(exportArray); // Logs correctly with all data with length 54
console.log(exportArray.length); // Logs as 0 for some reason
});
Ideally this should output the exportArray with it's data AND the length being 54. However it does output the data but the length is output as 0. (and yes I clicked on the data array in console and it shows a length of 54)
Why does the array get populated but I'm unable to use methods on it like exportArray.length correctly?
You must call Promise.all on promises after it has been filled with promises, which happens asynchronously in a then callback. Now you execute it synchronously when none of that has happened yet.
So do:
db.collection('lists').doc('List 1').collection("members").get().then(function(querySnapshot) {
let promises = querySnapshot.docs.map(function(doc) { // <-- use docs.map
return doc.data(); // <-- just return `data()`. No need for a new promise
});
// Must be here:
return Promise.all(promises).then(function (exportArray) { // <--- data arg!
console.log(exportArray);
console.log(exportArray.length);
});
});
Notes:
There is no need for new Promise when you have the value to resolve with readily available.
Instead of forEach, get the array from the query snapshot with .docs and the JS built-in .map().
The fact that you see the array in the console but with a length 0 is the behaviour of the console: it only logs the reference to the array, but then when you expand it in the console, it has in the mean time been populated; so you see the data. But it was not there at the moment of the logging, which is what the length: 0 is telling you.
Simplification
According to the firebase documentation, doc.data() returns the data, not a promise, so there is no reason to use Promise.all, a simple map should suffice:
db.collection('lists').doc('List 1').collection("members").get().then(function(querySnapshot) {
return querySnapshot.docs.map(function(doc) {
return doc.data();
});
}).then(function (exportArray) {
console.log(exportArray);
console.log(exportArray.length);
});
Since db.collection('lists').doc('List 1').collection("members").get() returns immediately with a promise that resolves only after the query completes, your code will go on to execute Promise.all() against an empty list and also return immediately because there's nothing to wait on. Some time after that, your snapshots will be ready and promises will be populated.
You should call Promises.all() only after the entire array has been populated.
I'm not very familiar with the db you use, but just fixing the antipatterns in your code should solve your problem. I guess that's about what your code should look like.
db.collection('lists')
.doc('List 1')
.collection("members")
.get()
.then(querySnapshot => querySnapshot.map(doc => doc.data())
.then(promises => Promise.all(promises))
.then(exportArray => {
console.log(exportArray); // Logs correctly with all data with length 54
console.log(exportArray.length); // Logs as 0 for some reason
})
About the antipattern:
avoid using new Promise() and similar. it's rarely necessary, ususally you already have a Promise chanin you can derive from.
Promises are wrapping values that you don't have yet. Don't try to "unwrap" these values by doing something along the lines of somePromise.then(value => { externalVariable = value; }) or in your case it's an Array.
You now have a variable that will (at some point in the future) contain the value you want, but at the moment is empty/invalid. So now you have to implement your own state management to check when the value has become valid; basically duplicating most of the logic of the Promise ;)