在每次迭代期间迭代并进行异步调用的最佳方法

时间:2015-07-31 02:46:06

标签: javascript node.js asynchronous

如果你必须在我的情况下循环并对存储库或网关进行一堆调用,我该如何异步地执行此操作意味着不在同步for循环中包装我的异步调用?

例如,更好的方法(重构此代码)循环遍历一组id,并调用find(),就像我试图做的那样?

目标:我想获取一组id,迭代它们,并在每次迭代中,使用id在我的网关上调用find()来获取该id的对象,然后把它塞进一个最终的数组中,当我说完所有的时候我会回来。

我正在使用

  • q(承诺)
  • co-pg(点击数据库)

someModule.js

var _gateway = require('./database/someGateway');

          var cars = [];
          var car;

             for (var i = 0; i < results.docs.length; i++){

                 var carId = results.docs[i].carId;

                 _gateway.find(carId)
                 .then(function(data){
                         console.log('data[0]: ' + data[0].id);
                         cars.push(data[0]);
                     })
                 .done();
             }

console.log("cars: " + cars.length); // length here is 0 because my asyn calls weren't done yet
             result(cars);

someGateway.js

'use strict';
var Q = require('q');

var _carModel = require('../../models/car');

module.exports = {
    models: {
        car: _carModel
    },
    find: _find
};

function _find(carId)
{
    return _carModel.find(carId);
};

carModel.js

'use strict';

var Q = require('q');
var pg = require('co-pg')(require('pg'));
var config = require('../../models/database-config');

var car = module.exports = {};

car.find = Q.async(function *(id)
{
    var query = 'SELECT id, title, description FROM car WHERE id = ' + id;

    var connectionResults = yield pg.connectPromise(config.connection);

    var client = connectionResults[0];
    var done = connectionResults[1];

    var result = yield client.queryPromise(query);
    done();

    console.log("result.rows[0].id: " + result.rows[0].id);
    return result.rows;
});

所以我需要帮助理解如何在someModule.js中重构我的代码以使其正常工作,以便我为每个id调用find(),将每个找到的汽车放入数组中,然后返回数组。 carModel代码是异步的。它会转到物理数据库以执行实际的查询查找。

更新#1

好几个小时后尝试了各种各样的sh **(q.all(),以及大量其他回调代码组合等),这就是我现在所拥有的:< / p>

someModule.js

var _data;
var Q = require('q');
var _solrClient = require('../models/solr/query');
var _solrEndpoint = "q=_text&indent=true&rows=10";
var _postgreSQLGateway = require('./database/postgreSQLGateway');

module.exports = {
    data: function(data){
        _data = data;
    },
    find: function (text, result){

        if(!searchText){
            result(null);
        };

         _solrClient.query(endpoint, function(results){

             var carIds = [];
             var cars = [];
             var car;

             for (var i = 0; i < results.docs.length; i++){
                 carIds.push(results.docs[i].carId);
             }

             for (var i = 0; i < carIds.length; i++) {

                 var car = _postgreSQLGateway.find(carIds[i], function(o){
                     console.log("i: " + i);
                 });
             };
        });
    }
};

someGateway.js

'use strict';
var Q = require('q');

var _carModel = require('../../models/postgreSQL/car');

module.exports = {
    models: {
        car: _carModel
    },
    find: _find
};

function _find(carId, foundCar)
{
    console.log("CALL MADE");

    _carModel.find(carId)
        .then(function(car){
            console.log("car: " + car[0].id);

            foundCar(car);
        });
};

carModel.js

[same code, has not changed]

当然我注意到for循环触发了我的所有函数调用asyncronously,所以当我console.write i时,它是10,因为for循环完成了但是我们知道,其余的console.logs在回调完成后发生。

所以我仍然无法正常工作......

当我在玩耍的时候,我开始沿着这条路走下去,但它在一堵砖墙上结束了:

var find = Q.async(function(carIds, cars)
{
    var tasks = [];
    var foundCars = [];

    for (var i = 0; i < carIds.length; i++) {
        tasks.push(_postgreSQLGateway.find(carIds[' + i + ']));
    };

    Q.all([tasks.join()]).done(function (values) {
        for (var i = 0; i < values.length; i++) {
            console.log("VALUES: " + values[0]);
            foundCars.push(values[0]);
        }
        cars(foundCars);
    });
});

我每次都以[物品承诺]结束价值[i]而不是汽车价值[i]

2 个答案:

答案 0 :(得分:3)

我不知道Q promises库,但这里是使用内置于node.js中的泛型Promise的解决方案。这会并行运行所有请求,然后在收集完所有结果后,它会运行最终的.then()处理程序并显示所有结果:

var _gateway = require('./database/someGateway');

var promises = [];
for (var i = 0; i < results.docs.length; i++) {
    promises.push(_gateway.find(results.docs[i].carId).then(function (data) {
        console.log('data[0]: ' + data[0].id);
        return data[0];
    }));
}
Promise.all(promises).then(function(cars) {
    // cars will be an array of results in order
    console.log("cars: " + cars.length);
    result(cars);
});

个人承诺库(比如我认识的Bluebird)具有内置功能,可让您以更少的代码执行此类活动,但我故意将此答案保留为仅使用标准承诺功能。

答案 1 :(得分:2)

使用es6中的vanilla Promise API(由Bluebird和其他lib复制)可能非常容易。首先将ID映射到promises数组:

var promises = results.docs.map(function(doc) {
  return _gateway.find(doc.carId);
});

然后为聚合结果创建一个承诺:

var allDone = Promise.all(promises);

然后在聚合承诺的done()回调中,您将获得最终结果数组,其长度和顺序与carId数组相同:

allDone.then(function(results) {
  // do something with "results"
});