A little confused on how best to write this - hope this description is clear.
I have a forEach function in which I go through some JS object data, and for each item i execute a function:
found.forEach(function(item) {
processData(item['userID']);
});
Within this processData
function I am using a MongoDB find() call.
var processData = function(userIDSelected) {
User.find( {_id: userIDSelected},
{gender: 1, country:1}, function(req, foundUser) {
processUserInfo(foundUser[0]['gender']);
});
}
The issue I have is how do i wait for everything in forEach to complete considering that each call will be running processUserInfo
in turn.
I've looked at using the Q library and Q.all however that doesnt work.
Is there a Q function that waits for everything in the long chain to complete?
Thanks
Q.all
:
Returns a promise that is fulfilled with an array containing the fulfillment value of each promise, or is rejected with the same rejection reason as the first promise to be rejected.
or Q.allSettled
:
Returns a promise that is fulfilled with an array of promise state snapshots, but only after all the original promises have settled, ie become either fulfilled or rejected.
So you'd do three things:
Modify processData
and possibly your call to MongoDB so that you end up with processData
returning a promise for the asynchronous operation. (Apologies, I'm not familiar with MongoDB. Alnitak says that with modern versions, if you don't supply a callback, MongoDB returns a promise (nice!). Otherwise, this question and its answers may help with returning a promise for a callback-based API.)
Use map
instead of forEach
to get an array of the resulting promises.
Use Q.all
or Q.allSettled
on that array of promises.
If User.find
returns a promise when no callback is specified, #1 looks something like this:
var processData = function(userIDSelected) {
return User.find(
{_id: userIDSelected},
{gender: 1, country:1}
).then(function(req, foundUser) { // <== Check these args with the MongoDB docs!
return processUserInfo(foundUser[0]['gender']);
});
};
If not, you can do it yourself with Q.defer
:
var processData = function(userIDSelected) {
var d = Q.defer();
User.find(
{_id: userIDSelected},
{gender: 1, country:1},
function(req, foundUser) {
processUserInfo(foundUser[0]['gender']);
d.resolve(/*...data could go here...*/); // You'd use d.reject() if there were an error
});
return d.promise;
};
Then here's what 2 and 3 look like:
Q.all(found.map(function(item) { // Or Q.allSettled
return processData(item);
}))
.then(...)
.catch(...);
If processData
only ever uses its first argument (ignoring any extra ones), you can ditch the intermediary function:
Q.all(found.map(processData)) { // Or Q.allSettled
.then(...)
.catch(...);
...but only if processData
ignores extra args, as map
will pass it three (the value, its index, and the array).
The way you coded I can assume that userID
is an ObjectId from MongoDB.
If that is the case, this is going to work as long as found
is not empty, otherwise your users would wait for the server response forever.
processData(
// Retrieve an object
// {
// $in: [ObjectId, ObjectId, ...]
// }
//
found.reduce(
function(query, user) {
return query.$in.push(user.userID), query;
},
{$in: []}
)
);
You can have more info about the $in
operator here .
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.