在这种情况下对Node.js中的函数进行阻塞调用?

时间:2014-05-27 01:51:19

标签: javascript node.js

我开始学习node.js.我在这里遇到了问题。我正在呼叫一个返回JSON的天气服务(下面的网址)。

http://api.wunderground.com/api/Your_key/conditions/q/CA/San_Francisco.json

我想在HTML页面上显示api的结果。

我编写了以下代码(getInfo模块)来检索并返回JSON。

var fbResponse; 

module.exports = function (url) {

var http=require('http');


http.get(url, function(res) {
    var body = '';

    res.on('data', function(chunk) {
        body += chunk;
    });

    res.on('end', function() {
        fbResponse = JSON.parse(body);  
        console.log("Got response: ", fbResponse.response);


    });

}).on('error', function(e) {
      console.log("Got error: ", e);
});


return fbResponse;
};

为了使用这个模块,我创建了一个test.js文件,如下所示:

var relay = require('./getInfo');
var url = 'http://api.wunderground.com/api/Your_key/conditions/q/CA/San_Francisco.json';
var response;
var x=relay(url);

console.log (x);

输出如下:

undefined                //  console.log(x) from test.js
Got response:  { version: '0.1',
  termsofService: 'http://www.wunderground.com/weather/api/d/terms.html',
  features: { conditions: 1 } }

测试代码中的控制台输出首先运行,其中没有数据。 HTTP get稍后竞争并显示我需要的实际输出。

如何修改测试代码以进行阻塞调用,以使测试代码中的var x实际上具有JSON输出而不是未定义?

如果没有对getInfo模块的阻塞调用,我能否实现所需的结果?

3 个答案:

答案 0 :(得分:5)

如您所知,node是异步的,因此http.getres.on('end', ..的回调将在relay函数执行后触发并返回。所以通常你不能从中返回结果。

您有几个选择:

  • 将回调传递给relay并使用:

    module.exports = function (url, cb) {
        http.get(url, function(res) {
    
            var body = '';
            res.on('data', function(chunk) {
                body += chunk;
            });
    
            res.on('end', function() {
                cb(null, JSON.parse(body));
            });
    
        }).on('error', cb);
    };
    

    然后像这样使用它:

    var relay = require('./getInfo');
    relay(url, function (err, x) {
        if (err) {
            console.error('oohh i got a error: ', err)    
        }
        console.log('oohh i got a response: ', x)    
    });
    
  • 使用承诺。这与传递回调几乎相同。稍微不那么轻巧,但在组合不同的异步操作时,您将了解它们有多棒。对于一个异步调用,可能没有任何区别。我在这里使用q。你也可以使用bluebird,这种方式更有效,但缺少q的糖。您可以阅读this article以了解为什么承诺在某些情况下比回调更清晰。

    module.exports = function (url) {
        var deferred = Q.defer();
        http.get(url, function(res) {
            var body = '';
            res.on('data', function(chunk) {
                body += chunk;
            });
    
            res.on('end', function() {
                deferred.resolve(JSON.parse(body));
            });
    
        }).on('error', function(e) {
            deferred.reject(e);
        });
        return deferred.promise;
    };
    
    
    
    var relay = require('./getInfo');
    relay(url).then(function responseHandler(x) {
        console.log('my awesome response')
    }, function errorHandler(err) {
        console.error('got an error', err);
    });
    
  • 使用生成器。它是Ecmascript 6规范的一部分,它仅存在于节点v0.11.x及更高版本中。但这几乎就是你想要的。

    根据过去的承诺示例,我们可以这样做:

    Q.async(function *() {
        var relay = require('./getInfo');
        var x = yield relay(url);
        console.log('my awesome response', x)
    });
    

    这几乎就是你想要的。您还可以使用co library的回调解决方案来实现它:

    co(function *() {
        var relay = require('./getInfo');
        var x = yield relay.bind(null, url);
        console.log('my awesome response', x);
    });
    

    您也可以在上面的示例中使用node-fibers,这几乎就像生成器一样。

    如果您想使用前沿Javascript,可以使用Async/Await代替带有承诺的生成器。

答案 1 :(得分:2)

您需要传入回调:

var http = require('http');

module.exports = function(url, cb) {
    http.get(url, function(res) {
        var body = '';

        res.on('data', function(chunk) {
            body += chunk;
        });

        res.on('end', function() {
            var resp, err;
            try {
              resp = JSON.parse(body);
            } catch (ex) {
              err = ex;
            }
            cb(err, resp);
        });

    }).on('error', function(e) {
          console.log("Got error: ", e);
          cb(e);
    });
};

然后使用它:

var relay = require('./getInfo');
var url = 'http://api.wunderground.com/api/Your_key/conditions/q/CA/San_Francisco.json';
var response;
relay(url, function(err, resp) {
  if (err) throw err; // TODO: handle better
  console.dir(resp);
});

答案 2 :(得分:0)

您可以在模块函数中进行回调以返回结果。

module.exports = function (url, onsuccess) {
    ...

    res.on('end', function() {
        fbResponse = JSON.parse(body);
        if(onsuccess){
            onsuccess(null, fbResponse);
        }

然后在你的来电代码中:

relay(url, function(err, result){
    console.log(result);
});

另一种选择是使用httpsync模块,该模块提供同步 apis,以实现'http'模块提供的相同功能。但是在节点js编程中,你应该总是避免同步调用