从回调函数返回数组

时间:2015-08-26 00:22:01

标签: node.js

经过几个小时的思考和googleing,我似乎无法找到解决这个愚蠢的简单问题的方法!

我正在尝试创建一个JSON对象数组,以便将它们插入到DB中。但是由于Nodejs的异步方面,它变得有点复杂。

我有什么:

function foo (arg) {
    request({url: arg}, function(err, response, results){
        var chunk = [];
        var r = JSON.parse(results);
        (r.bar).forEach(function(item){
            geocoder.reverse({lat: item.lat, lon: item.lng}, function(err, res){
                chunk.push(res[0].formattedAddress;
            });
        });
        console.log(chunk); \\ This is eventually empty!
        insertData(chunk); \\ useless due to chunk being empty!
    });

我想知道如何才能实现这一点(构建一个数组并将其插入到数据库中)?

我很绝望,请帮忙 提前谢谢,

3 个答案:

答案 0 :(得分:2)

我建议安装async模块,因为它对node.js来说非常有用。

第一:

npm install --save async

然后:

var async = require('async');

function foo (arg) {
    request({url: arg}, function(err, response, results){
        var r = JSON.parse(results);
        async.map(r.bar, function eachItem (item, cb) {
            geocoder.reverse({lat: item.lat, lon: item.lng}, function(err, res){
                cb(null, res[0].formattedAddress);
            });
        }, 
        function done (err, result) {
            console.log(result); \\ this will be your ordered array
            insertData(result); \\ no longer useless!
        });
    });

答案 1 :(得分:1)

你必须等待geocoder.reverse函数完成每个r.bar - 创建一个计数器,每次完成调用时递增,然后在最后一次调用完成后,insertData!< / p>

function foo (arg) {
    request({url: arg}, function(err, response, results){
        var chunk = [];
        var r = JSON.parse(results);
        var totalCalls = r.bar.length;
        var completedCalls = 0; 

        (r.bar).forEach(function(item){
            geocoder.reverse({lat: item.lat, lon: item.lng}, function(err, res){
                chunk.push(res[0].formattedAddress); //missing ) here in your code
                completedCalls++;
                if (completedCalls == totalCalls) {
                    insertData(chunk);
                }
            });
        });
    });
}

答案 2 :(得分:1)

这是一个使用promises来管理异步操作并保留多个结果顺序的解决方案:

var Promise = require('bluebird');
var requestAsync = Promise.promisify(request);
var geocoder.reverseAsync = Promise.promisify(geocoder.reverse);

function foo(arg) {
    return requestAsync({url: arg}).spread(function(response, results) {
        var r = JSON.parse(results);
        return Promise.map(r.bar, function(item) {
            return geocoder.reverseAsync({lat: item.lat, lon: item.lng}).then(function(res) {
                return res[0].formattedAddress;
            });
        });
    });
}

foo(something).then(function(addresses) {
    // addresses will be an array of addresses
    insertData(addresses);
}, function(err) {
    // some error occurred
});

以下是对其工作原理的解释:

  1. 加载bluebird promise library
  2. 创建此处使用的两个异步函数的promisified版本,并为它们提供&#34; Async&#34;后缀来区分它们。
  3. 致电requestAsync()并使用.spread()代替.then(),以便更轻松地将所返回的两个值分开
  4. 使用Promise.map()为我们迭代数据数组并返回代表所有子承诺的单一承诺,以便我们知道所有地理编码器操作何时完成。
  5. Promise.map()迭代器回调中,调用geocoder.reverseAsync()并使用.then()访问结果。
  6. 转到geocoder结果,返回formattedAddressPromise.map()将成为foo()
  7. 中每个承诺的返回值
  8. 然后,在调用.then()时使用Promise.map()来设置您的成功和错误处理程序。在成功处理程序中,您可以按顺序访问地址数组。
  9. 仅供参考,如果您担心遇到可能同时运行多少个同步地理编码器调用的速率限制问题,您可以向 return Promise.map(r.bar, function(item) { ... }, {concurrency: 3}); 添加并发选项,如下所示:

    "["