通过for循环

时间:2017-01-05 07:54:58

标签: arrays node.js asynchronous sails.js

我正在使用在nodejs(6.9.1)上运行的风帆(0.11.0)。我试图通过for循环填充它来构造一个数组。我会发送这个完成的数组以响应客户端。我已经尝试了Stack Overflow上人们建议的各种方法,例如

讨论here建议

for (var i = yearStart; i < yearEnd+1; i++) {
    arr.push(i);
}

this讨论中,建议使用:

var array = calendars.map(function(item) {
    return item.id;
});

console.log(array);

类似地,我尝试了很多方法,但是我遇到了同样的问题,即在循环期间,数组被填满但是一旦循环完成,数组因异步进程而变空,因此我无法发送响应。为了解决这个问题,我尝试检查循环体内的索引,并通过

从循环体内部发送响应
var userArray = [];
_.each(users, function(user, index){
  MySQLConnector.query('CALL user_image (?)', [user.id], function(err, userImage){
    if(err){
      return res.json({"status":"some_error"});
    }else{
      userID = user.id
      userImageID = userImage[0][0].id;
      var userInfo = {
        userID: userID,
        userImageID: userImageID
      }
      userArray.push(userInfo)
      if(index == users.length - 1){
          res.json({selectedUsers: userArray});
      }
    }
  });
});

我正在启动空 userArray ,然后遍历用户对象,其中对象的每个元素都以名称用户索引即可。通过MySQL查询,我获取了 userImage 对象,在每次迭代中,我创建了一个名为 userInfo 的对象,其中包含 userID userImageID 即可。我正在将此对象推送到 userArray 。在for循环(_.each)的每次迭代之后,我检查是否达到了最后一个索引。达到最后一个索引后,最终数组将在循环体完成之前作为响应发送。

我也有一个问题,即阵列主体并不总是完全填满。原因是由于异步过程,索引并不总是遵循0,1,2,3,4 ......的顺序,它可以从任何数字开始,并可以跳转到任何索引在下一次迭代中,例如,要开始的第一个索引将是4,第二个将是0,第三个将是2,依此类推。每次运行for循环时,此序列都会有所不同。对于用户来说,它似乎是一个完全随机的过程。因此,如果 users.length 为8,并且当前索引在第三次迭代时随机为7,则将满足条件index == users.length - 1,并且仅使用由3个元素组成的数组发送响应,而不是8.

有人可以建议我通过nodejs中的for循环填充数组的更好更强大的方法,并在响应中发送该数组,以便所有项目按原始顺序包含在数组中吗?

2 个答案:

答案 0 :(得分:2)

当您使用节点j时,最好使用任何承诺库(如bluebirdasync)来处理异步请求。 你的循环没有按预期工作的原因是因为正如你所指出的那样,由于异步请求需要时间来解决_.each循环没有等待的问题。

使用bluebird,可以使用Promise.map方法完成,该方法的工作原理如下文所述:

  

给定Iterable(数组是Iterable),或Iterable的承诺,   它产生承诺(或承诺和价值的混合),迭代   将Iterable中的所有值转换为数组并将数组映射到   另一个使用给定的映射器函数。

     

等待映射器函数返回的Promise和   在所有映射的承诺都有之前,返回的承诺不会实现   也实现了。如果阵列中的任何承诺被拒绝,或者任何承诺   由mapper函数返回的promise被拒绝,返回   承诺也被拒绝。

因此,使用Promise.map您的代码可以更新如下:

var Promise = require("bluebird");     

return Promise.map(users, function(user, index){
  return MySQLConnector.query('CALL user_image (?)', [user.id], function(err, userImage){
    if(err){
      return Promise.reject({"status":"some_error"});
    }else{
      userID = user.id
      userImageID = userImage[0][0].id;
      var userInfo = {
        userID: userID,
        userImageID: userImageID
      }
      return userInfo;
    }
  });
})
.then(function (usersArray){
  res.json({selectedUsers: usersArray});
})
.catch(function (err){
  res.json(err);
});

答案 1 :(得分:1)

您可以使用SynJS

同步执行带回调功能的循环
var SynJS = require('synjs');
var mysql      = require('mysql');
var connection = mysql.createConnection({
  host     : 'localhost',
  user     : 'tracker',
  password : 'tracker123',
  database : 'tracker'
});


function myFunction1(modules,connection,users) {
    var ret=[];
    for(var i=0; i<users.length; i++) {
        connection.query("SELECT CONCAT('some image of user #',?) AS userImage", [users[i]], function(err, rows, fields) {
              if (err) throw err;

              ret.push({
                  id: users[i],
                  image:    rows[0].userImage
              });
              modules.SynJS.resume(_synjsContext); // <-- indicate that callback is finished
        });
        SynJS.wait(); // <-- wait for callback to finish
    }
    return ret;
};

var modules = {
        SynJS:  SynJS,
        mysql:  mysql,
};

var users = [1,5,7,9,20,21];

SynJS.run(myFunction1,null,modules,connection,users,function (ret) {
    console.log('done. result is:');
    console.log(ret);
});

结果如下:

done. result is:
[ { id: 1, image: 'some image of user #1' },
  { id: 5, image: 'some image of user #5' },
  { id: 7, image: 'some image of user #7' },
  { id: 9, image: 'some image of user #9' },
  { id: 20, image: 'some image of user #20' },
  { id: 21, image: 'some image of user #21' } ]