I have users
list in database
:
let users = []; // Please check the full list at the end of this post
Here is my findUsers
function: (This function looks like search from database. But I just write the simple function with hard-code data)
function findUsers(user) {
return new Promise(function (resolve) {
resolve(user.users);
});
}
I want to get all of user which begin by userId:
const walkUsers = (user, list) => {
let usrLst = user.users;
list = list || [];
usrLst.forEach((usr) => {
if (usr.users.length > 0) {
list = walkUsers(usr, list);
}
list.push(usr);
});
return list;
};
This function return right result.
[
{ id: '009', name: 'User 9', users: [] },
{ id: '011', name: 'User 11', users: [] },
{ id: '010', name: 'User 10', users: [ [Object] ] },
{ id: '004', name: 'User 4', users: [ [Object], [Object] ] },
{ id: '003', name: 'User 3', users: [ [Object] ] },
{ id: '007', name: 'User 7', users: [] },
{ id: '002', name: 'User 2', users: [ [Object], [Object] ] },
{ id: '008', name: 'User 8', users: [] },
{ id: '005', name: 'User 5', users: [ [Object] ] },
{ id: '006', name: 'User 6', users: [] }
]
But this is not my expected. I want to fetch data from database (Mongoose)
const walkUsers = (user, list) => {
return findUsers(user)
.then((usrLst) => {
list = list || [];
usrLst.forEach((usr) => {
if (usr.users.length > 0) {
walkUsers(usr, list).then((rtnLst) => {
console.log("rtnLst");
console.log(rtnLst);
return rtnLst;
});
}
list.push(usr);
});
return list;
});
};
And this function is missing user 009
, 010
, 011
[
{ id: '002', name: 'User 2', users: [ [Object], [Object] ] },
{ id: '005', name: 'User 5', users: [ [Object] ] },
{ id: '006', name: 'User 6', users: [] },
{ id: '003', name: 'User 3', users: [ [Object] ] },
{ id: '007', name: 'User 7', users: [] },
{ id: '008', name: 'User 8', users: [] },
{ id: '004', name: 'User 4', users: [ [Object], [Object] ] }
]
I could not figure out what am I wrong. Could you please help me check it out?
Let check users
data:
let users = [
{
id: '001',
name: 'User 1',
users: [
{
id: '002',
name: 'User 2',
users: [
{
id: '003',
name: 'User 3',
users: [
{
id: '004',
name: 'User 4',
users: [
{
id: '009',
name: 'User 9',
users: []
},
{
id: '010',
name: 'User 10',
users: [
{
id: '011',
name: 'User 11',
users: []
},
]
},
]
}
]
},
{
id: '007',
name: 'User 7',
users: []
}
]
},
{
id: '005',
name: 'User 5',
users: [
{
id: '008',
name: 'User 8',
users: []
}
]
},
{
id: '006',
name: 'User 6',
users: []
},
]
},
];
You need to make the following changes:
usrLst.forEach()
loop, accumulate the list of promises you get and then use Promise.all()
so you know when they are all done. Right now, you are not keeping track of when your calls to walkUsers()
are done at all so, depending upon timing, you can lose or miss some results..then()
handler so it is chained to the parent findUsers()
promise that walkUsers()
is returning. That could look like this:
const walkUsers = (user, list) => {
return findUsers(user)
.then((usrLst) => {
list = list || [];
var promises = [];
usrLst.forEach((usr) => {
if (usr.users.length > 0) {
promises.push(walkUsers(usr, list));
}
list.push(usr);
});
return Promise.all(promises).then(function() {
// make the list be the resolved value from this promise
return list;
});
});
};
Keep in mind that any time you have an asynchronous operation inside a .then()
handler, a promise should nearly always be returned from the .then()
handler so that internal async operation is chained to the parent operation and the caller will not be told things are done until all the nested async operations are done. If not, that internal async operation just becomes an orphan async operation that runs on its own time and nothing above it waits for it or coordinates with it and whether or not the caller sees its result will be a matter of timing luck and how your code works (which makes an uncertain result).