[英]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.