【问题标题】:How to gather the result of Web APIs on nodeJS with 'request' and 'async'如何使用“请求”和“异步”在节点 JS 中收集 Web API 的结果
【发布时间】:2013-10-08 23:57:03
【问题描述】:

我正在编写一个测试代码,它使用“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[] 中得到结果?

【问题讨论】:

    标签: javascript node.js asynchronous async.js


    【解决方案1】:

    好吧,让我给你介绍一下async的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 是如何工作的,试试这个

    【讨论】:

      【解决方案2】:

      您在这里将 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);
        });
      };
      

      【讨论】:

      • 让我确认一下,'result' 是否必须仅在 async.map 'function(err, results){}' 中处理?
      • async.map 函数获取地址数组并对每个项目执行迭代器函数。每次调用迭代器函数时,它都会回调(err, result),其中结果是该地址的映射输出。 async.map 的完成回调接受 (err, results) 参数,其中 results 是映射的输出,与地址数组的顺序相同。
      • 查看我的最新编辑以获取手动逐个添加结果的替代方法
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-12-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多