在nodejs

时间:2017-01-24 06:07:22

标签: node.js express recursion mongoose

我正在开发MEANJS项目。这是一个通过递归获取数据返回数组的函数。现在,我遇到了如何获取该数组的问题。

问题: -

让用户 A 拥有{_id:' 001',rmUserId:' 001'}, B :{ _id:' 002',rmUserId:' 001'}, C {_id:' 003',rmUserId:' 002& #39;}, D {_ id:' 003',rmUserId:' 001'}, E {_id:&# 39; 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.OutgoingMessage.setHeader(_http_outgoing.js:346:11)       在ServerResponse.header(/home/clsah/projects/LMS/node_modules/express/lib/response.js:719:10)   ........ .......

4 个答案:

答案 0 :(得分:2)

如果您利用mongoose更高版本中内置的promise并在模块中显示promise接口,则可以执行以下操作:

此处模拟运行代码:https://jsfiddle.net/jfriend00/zr6ynmsu/

<Grid
    Background="Azure"
    BorderBrush="Pink"
    BorderThickness="2">
    <Grid.ColumnDefinitions>
        <ColumnDefinition x:Name="col1" Width="*" />
        <ColumnDefinition Width="Auto" />
    </Grid.ColumnDefinitions>
    <TextBlock        
        Grid.Column="0"  
        x:Name="txt1"
        Foreground="Blue"
        Text="layouttest1layouttest1layouttest1layou"
        TextWrapping="Wrap"  />
    <TextBlock     
        Grid.Column="1"           
        Foreground="Red"
        x:Name="txt2"
        Text="layouttest2layouttest2layouttest2layouttest2layout"
        TextWrapping="Wrap" />     
    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup>
            <VisualState>
                <VisualState.StateTriggers>
                    <AdaptiveTrigger x:Name="adptivetrigger"   />
                </VisualState.StateTriggers>
                <VisualState.Setters>
                    <Setter Target="col1.Width" Value="Auto" />
                </VisualState.Setters>
            </VisualState>
        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>
</Grid>

然后,你会像这样使用它:

 private void Page_Loaded(object sender, RoutedEventArgs e)
 {
     var testwidth = txt1.ActualWidth + txt2.ActualWidth;
     adptivetrigger.MinWindowWidth = testwidth;          
 }

如果你仍然想在getUnderUsersByRm上使用你的回调接口,你仍然可以这样做(尽管如果你做的不仅仅是一些异步调用,那么真的值得使用所有异步操作的promises):

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);
}

如果您的用户树是循环的,那么您可以通过跟踪所有访问过的用户来防止无限循环。您需要某种识别每个用户的唯一密钥。由于我不知道你的程序中有什么,我将假设你传入的用户已经是id。唯一标识用户的任何属性都将在此方案中起作用:

const someModule = require('yourModule');

someModule.getUnderUsersByRm(someUser).then(results => {
    // process all results here
}).catch(err => {
    // error here
});

答案 1 :(得分:1)

使用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);
  }
}

答案 2 :(得分:1)

我尝试使用基于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]为您的例子。

答案 3 :(得分:0)

我很确定你收到了这个错误,因为你运行__getUserByRmId函数(如果发生错误,里面会调用callback函数,然后,除了发生的任何事情,你调用{ {1}}再次运行,可能会向同一个请求发送多个响应,依此类推,即使已经发送了响应,也会多次设置标头。

我应该在评论中发帖,但没有足够的声誉这样做