通过for循环在nodejs中构造数组

Posted

技术标签:

【中文标题】通过for循环在nodejs中构造数组【英文标题】:Construct array in nodejs through for loop 【发布时间】:2017-05-19 16:34:03 【问题描述】:

我正在使用在 nodejs (6.9.1) 上运行的sails (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,然后遍历 users 对象,其中对象的每个元素都由名称 user 和一个 索引。通过 MySQL 查询,我获取 userImage 对象,并且在每次迭代中,我创建了一个名为 userInfo 的对象,该对象由 userID 组成>userImageID。我将这个对象推入 userArray。在 for 循环 (_.each) 的每次迭代之后,我检查是否达到最后一个索引。到达最后一个索引后,在循环主体完成之前将最终数组作为响应发送。

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

有人可以建议我一种更好更健壮的方法来通过 nodejs 中的 for 循环填充数组并发送该数组作为响应,以便所有项目都按原始顺序包含在数组中?

【问题讨论】:

它不会按照顺序进行,我认为您也会遇到与地图相同的问题。看看 lodash 和 bluebird 中的 reduce,我知道后者会保证订单,我认为 lodash 会是一样的,但可能是错误的。 再想一想,在异步库中,我经常使用eachSeries,这也保证了顺序,并且可能是您现有代码的解决方案。 【参考方案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'  ]

【讨论】:

【参考方案2】:

当您使用 node js 时,最好使用任何 promise 库,例如 bluebirdasync 来处理异步请求。 您的循环未按预期工作的原因是因为正如您所指出的,由于异步请求需要时间来解决 _.each 循环未等待的问题。

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

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

等待 mapper 函数返回的 Promise 并且 在所有映射的 Promise 都完成之前,返回的 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);
);

【讨论】:

谢谢,准备试试promise库。 我仍然得到响应数组为[null,null,null,null,null,null] 它应该可以工作,也许你得到的响应是空的。能否在成功回调中返回userInfo 之前放一个日志并检查一次。 是的,这个空数组来自日志本身。 查询后有没有查看userImage的值?

以上是关于通过for循环在nodejs中构造数组的主要内容,如果未能解决你的问题,请参考以下文章

如何在 NodeJS 中使用 for 循环插入 SQL 表?

如何在for循环中等待每次迭代并在nodeJS中将响应作为API响应返回

等待每个 for 循环迭代完成,然后在 nodejs 中开始下一个

为啥通过jQuery中的for循环后数组顺序是随机的? [复制]

hibernate入门---Hibernate查询方式(for循环构造器对象数组等)第三天,相当于总结整合

for循环内的回调函数-Nodejs