繁体   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