[英]Call synchronously recursively function in nodejs
我正在开发MEANJS项目。 这是一个通过递归获取数据返回数组的函数。 现在,我遇到了如何获取该数组的问题。
问题:-
让用户A拥有的属性是{_id:'001',rmUserId:'001'}, B :{_ id:'002',rmUserId:'001'}, C {_id:'003',rmUserId:'002'} , D {_id:'003',rmUserId:'001'}, E {_id:'004',rmUserId:'009'}
如果用户A将登录,则allUnderUsers
数组具有B,C,D个用户。 这意味着所有用户都必须遵循自己的层次结构。
A
/ \
B D
/
C
这是我的代码:
module.exports.getUnderUsersByRm = function(currentUser, callback) {
try {
var allUnderUsers = [];
function __getUserByRmId(rmId) {
User.find({ rmUserId: rmId, isAlive: true, status: 'active' })
.exec(function(err, users) {
if (err)
return callback(err)
if (users.length > 0) {
users.forEach(function(ele, i) {
allUnderUsers.push(ele);
__getUserByRmId(ele.rmUserId);
});
} else {
return false;
}
})
}
__getUserByRmId(currentUser._id);
} catch (e) {
callback(e)
}
}
在这里,我需要在调用所有递归函数之后获取allUnderUsers数组。
我有使用回调函数,如:
....
...
__getUserByRmId(currentUser._id);
callback(null,'done');
.
.
但它抛出一个错误,即
错误:发送标头后无法设置
.at ServerResponse.header(/home/clsah/projects/LMS/node_modules/express/lib/response.js:719:10)上的.at ServerResponse.OutgoingMessage.setHeader(_http_outgoing.js:346:11)...... .. .......
如果您利用猫鼬更高版本中内置的promise并在模块中显示promise接口,则可以执行以下操作:
在此处模拟运行代码: https : //jsfiddle.net/jfriend00/zr6ynmsu/
module.exports.getUnderUsersByRm = function(currentUser) {
function __getUserByRmId(rmId) {
// return promise here
return User.find({ rmUserId: rmId, isAlive: true, status: 'active' }).exec().then(function(users) {
if (users.length > 0) {
let promises = [];
users.forEach(function(ele, i) {
promises.push(__getUserByRmId(ele.rmUserId));
});
// return promise which will chain it to original promise
// this is the key to getting the master promise to wait
// for everything to be done
return Promise.all(promises).then(results => {
// add in previous results
// flatten all the results together into a single array
// and remove empty results
results.unshift(users);
return [].concat.apply([], results.filter(item => item.length > 0));
});
} else {
return [];
}
});
}
return __getUserByRmId(currentUser);
}
然后,您将像这样使用它:
const someModule = require('yourModule');
someModule.getUnderUsersByRm(someUser).then(results => {
// process all results here
}).catch(err => {
// error here
});
如果仍然希望在getUnderUsersByRm上使用回调接口,则仍然可以执行此操作(尽管如果要执行多个异步调用,那么对所有异步操作使用Promise确实值得):
module.exports.getUnderUsersByRm = function(currentUser, callback) {
function __getUserByRmId(rmId) {
// return promise here
return User.find({ rmUserId: rmId, isAlive: true, status: 'active' }).exec().then(function(users) {
if (users.length > 0) {
let promises = [];
users.forEach(function(ele, i) {
promises.push(__getUserByRmId(ele.rmUserId));
});
// return promise which will chain it to original promise
// this is the key to getting the master promise to wait
// for everything to be done
return Promise.all(promises).then(results => {
// add in previous results
// flatten all the results together into a single array
// and remove empty results
results.unshift(users);
return [].concat.apply([], results.filter(item => item.length > 0));
});
} else {
return [];
}
});
}
__getUserByRmId(currentUser).then(result => {
callback(null, result);
}).catch(err => {
callback(err);
});
}
如果您的用户树是循环的,则可以通过跟踪所有访问的用户来防止无限循环。 您需要某种唯一的密钥来标识每个用户。 由于我不知道程序中的内容,因此我假设您要传入的用户已经是一个ID。 任何可以唯一标识用户的属性都可以在以下方案中使用:
module.exports.getUnderUsersByRm = function(currentUser) {
let visitedUsers = new Set();
function __getUserByRmId(rmId) {
// return promise here
return User.find({ rmUserId: rmId, isAlive: true, status: 'active' }).exec().then(function(users) {
if (users.length > 0) {
let promises = [];
users.forEach(function(ele, i) {
// make sure we aren't already processing this user
// avoid circular loop
let userId = ele.rmUserId;
if (!visitedUsers.has(userId)) {
visitedUsers.add(userId);
promises.push(__getUserByRmId(userId));
}
});
// return promise which will chain it to original promise
// this is the key to getting the master promise to wait
// for everything to be done
return Promise.all(promises).then(results => {
// add in previous results
// flatten all the results together into a single array
// and remove empty results
results.unshift(users);
return [].concat.apply([], results.filter(item => item.length > 0));
});
} else {
return [];
}
});
}
return __getUserByRmId(currentUser);
}
async.until
并维护要处理的元素数组 var async = require('async');
module.exports.getUnderUsersByRm = function(currentUser, callback) {
try {
var allUnderUsers = [];
var usersToProcess = [currentUser._id]; // Array to track what was earlier done with recursion
async.until(function() { // Test function for async.until
return usersToProcess.length === 0;
}, function __getUserByRmId(callback2) { // fn for async.until
User.find({
rmUserId: usersToProcess.shift(), // Take first element of array
isAlive: true,
status: 'active'
})
.exec(function(err, users) {
if (err)
return callback2(err)
if (users.length > 0) {
users.forEach(function(ele, i) {
allUnderUsers.push(ele);
usersToProcess.push(ele.rmUserId);
// __getUserByRmId(ele.rmUserId); // To Remove
});
return callback2(); // Always call callback;
} else {
return callback2(); // Earlier: return false; Return some err argument if you want
}
})
}, callback); // Final callback for async.until
} catch (e) {
callback(e);
}
}
我试图使用基于async
的方法来实现该解决方案。 您可能会发现它很幼稚,但是应该可以使用。
function __getUserByRmId(rmId, cb) {
var allUnderUsers = [];
User.find({ rmUserId: rmId, isAlive: true, status: 'active' })
.exec(function(err, users) {
async.each(users, function(user, callback){
if (user._id != rmId){
// recursive call
__getUserByRmId(user._id, function(childUsers){
childUsers.forEach(function (childUser) {
allUnderUsers.push(childUser);
});
callback(); //intermediate callback for async call
});
} else { //condition check to avoid infinite loop
allUnderUsers.push(user);
callback(); //intermediate callback for-loop
}
}, function(err){
cb(allUnderUsers); //final callback with result
});
});
}
module.exports.getUnderUsersByRm = function(currentUser, callback) {
__getUserByRmId(currentUser._id, callback)
};
从逻辑上讲,它应该工作。 如果有任何问题,请尝试并让我知道。 现在,它还返回包含父对象的数组。 例如[A,B,C,D]。
我很确定您会收到该错误,因为您运行__getUserByRmId
函数(如果发生错误,该函数会在内部调用callback
函数,然后,除了发生的任何事情之外,您还会再次调用该callback
函数,这可能会将多个响应发送给一个相同的请求,依此类推,即使已发送响应,也要多次设置标头。
我应该在评论中发布此内容,但信誉不佳
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.