2

我正在编写一个测试代码,它通过使用“request.js”和“async.js”从 node.js 上的 Web API 收集结果。

这是我的示例代码;

var request = require('request');
var async = require('async');

var addresses = [
    "Tokyo",
    "Moscow",
    "Bagdhad",
    "Mountain View",
    "New York",
];

function accessUrl( address, callback ) {  
    options ={
        headers: {'user-agent': 'Mozilla/5.0'},
        url: 'http://api.openweathermap.org/data/2.5/weather?q=' + address,
        json: true
    };

    request.get(options, function(err, response, body_json) {
        if( !err && response.statusCode === 200 ){
            return callback(address, body_json);
        }
        else{
            console.log('error');
        }
    });
}

function showContent( address, body_json ) {
    console.log( "%s : %s, %s (C)", address, body_json['weather'][0]['main'],
                 Math.round(body_json['main']['temp']-273.15));
    return [ address, body_json['weather'][0]['main'],
                 Math.round(body_json['main']['temp']-273.15)];
}

var result = [];
async.map( addresses, function( item, callback ) {
    result.push (accessUrl( item, showContent ));
    }, function(err) {
    });

console.log(result);

然而,结果是;

~$ node asyncsample1.js
[ undefined, undefined, undefined, undefined, undefined ]
Tokyo : Clear, 23 (C)
Mountain View : Mist, 10 (C)
Bagdhad :  Clear, 10 (C)
New York : Clear, 22 (C)
Moscow : Clouds, 4 (C)

回调函数 showContent() 中的 console.log() 显示正确的结果,但收集的结果都是“未定义的”。

如何在 var result[] 中获得结果?

4

2 回答 2

4

好吧,让我向您介绍async's 的concat功能。这是您修改后的程序。

var request = require('request');
var async = require('async');

var addresses = [
    "Tokyo",
    "Moscow",
    "Bagdhad",
    "Mountain View",
    "New York",
];

function accessUrl( address, callback ) {  
    options ={
        headers: {'user-agent': 'Mozilla/5.0'},
        url: 'http://api.openweathermap.org/data/2.5/weather?q=' + address,
        json: true
    };

    request.get(options, function(err, response, body_json) {
        if( !err && response.statusCode === 200 ){
            return callback(null, [[ address, body_json['weather'][0]['main'],
                 Math.round(body_json['main']['temp']-273.15)]]);
        }
        else{
            return callback(err);
        }
    });
}

async.concat (addresses, accessUrl, function(err, result) {
    if (err) {
        console.error(err);
    } else {
        console.log(result);
    }
});

输出

[ [ 'Mountain View', 'Haze', 17 ],
  [ 'Bagdhad', 'Clear', 18 ],
  [ 'New York', 'Clear', 26 ],
  [ 'Tokyo', 'Clouds', 22 ],
  [ 'Moscow', 'Clouds', 3 ] ]

您不必自己同步结果。concat为你做。

如果你有兴趣了解 async 的 concat 是如何工作的,试试这个

在此处输入图像描述

于 2013-10-02T17:12:16.840 回答
3

您在这里将 undefined 推送到结果,因为 accessUrl 是异步的。它返回未定义。在执行 accessUrl 回调之前,您没有任何结果 - 但到那时,即使它返回结果, undefined 已经被推送并且结果无处可去。

// wrong
var result = [];
async.map( addresses, function( item, callback ) {
    result.push (accessUrl( item, showContent ));
    }, function(err) {
    });

任何依赖于异步函数结果的逻辑都必须放在该函数的回调中。此外,async为您处理输出数组,您只需传递(err, result给迭代器。

//corrected
async.map( addresses
, function( item, callback ){
  accessUrl( item, function(address, body_json){
    result = showContent(address, body_json);
    // tell async.map there was no error and this is the mapped result
    callback(null, result); 
  });
}
, function(err, results) {
  console.log(results)
});

如果我是你,我会做一些调整。首先处理存在请求错误的情况。其次,我喜欢done在 async 中用作回调的名称,因为我经常发现我在另一个异步函数中使用 async,而且我通常有一个callback已经调用的变量,它是父函数的回调。所以我会这样做:

//improved
function iterator(item, done){
  accessUrl( item, function(err, address, body_json){
  // you will have to tweak accessUrl to callback with three parameters instead of two

    if(err){ return done(err) }; 
    // the `return` is necessary, otherwise latter lines will still execute

    result = showContent(address, body_json);

    // tell async.map there was no error and this is the mapped result
    done(null, result); 
  });
};

async.map( addresses
, iterator
, function(err, results) {
  if(err){ console.log(err) };
  console.log(results)'
});

另一种方法是创建一个结果数组并逐个添加结果。这更接近您最初的想法,但由于async.map自动构建结果数组,因此将此策略与 async.map 一起使用没有意义。

//alternative
function printWeather(callback){
  var results = [];
  async.forEachSeries(addresses
  , function(item, done){
    accessUrl( item, function(err, address, body_json){
      if(err){ return done(err) }; 
      result = showContent(address, body_json);
      results.push(result);
      done(null); 
    });
  }
  , function(err) {
    if(err){ console.log(err) };
    console.log(results);
    //callback(err, results);
  });
};
于 2013-10-02T14:44:34.053 回答