【问题标题】:Make a blocking call to a function in Node.js required in this case?在这种情况下需要对 Node.js 中的函数进行阻塞调用?
【发布时间】:2017-09-13 22:00:20
【问题描述】:

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

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 模块的情况下获得所需的结果?

【问题讨论】:

    标签: javascript node.js


    【解决方案1】:

    众所周知,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 而不是带有 Promise 的生成器。

    【讨论】:

    • 像你这样的人使 Stackoverflow 成为一个不错的 wiki。一个包含所有经验的知识库都注入了简洁的答案。谢谢!
    • 这就是我想要做的:)
    【解决方案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);
    });
    

    【讨论】:

      【解决方案3】:

      您可以在模块函数中使用回调来返回结果。

      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' 模块提供的相同功能。但是在 node js 编程中,你应该总是避免同步调用

      【讨论】:

        猜你喜欢
        • 2013-02-20
        • 2020-11-08
        • 2018-06-24
        • 2015-03-13
        • 1970-01-01
        • 1970-01-01
        • 2017-01-20
        • 2015-12-08
        • 1970-01-01
        相关资源
        最近更新 更多