簡體   English   中英

在Node.js中同步遞歸調用函數

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

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM