在这种情况下需要阻止对Node.js中的函数的阻止调用?

我开始学习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; }; 

所以要使用这个模块,我创build了一个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 } } 

testing代码中的控制台输出首先运行,没有数据。 HTTP获得竞争,并显示我需要的实际输出。

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

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

如你所知,节点是asynchronous的,所以http.getres.on('end', ..的callback函数在执行完成后会被触发并返回,所以通常你不能返回结果。

你有几个select:

  • 传递一个callback来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) }); 
  • 使用承诺。 这与传递callback几乎相同。 less一点轻量级,但是当组合不同的asynchronous操作时,你会明白它们是多么的棒。 对于一个asynchronous调用,可能没有任何区别。 这里我用q 。 你也可以使用蓝鸟 ,这是更高效的方式,但缺乏一些糖的q。 您可以阅读这篇文章 ,了解为什么承诺在某些情况下比callback更清晰。

     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的callback解决scheme来实现它:

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

    在上面的例子中,你也可以使用节点光纤 ,这几乎是一个类似于发电机的工具。

    如果你想使用stream血的Javascript,你可以使用Async / Await而不是带有promise的生成器。

你需要传递一个callback,而不是:

 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); }); 

你可以在模块函数中进行callback来返回结果。

 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); }); 

另一种select是使用httpsync模块,该模块为“http”模块提供的相同function提供同步 apis。 但是在js节点编程中,你应该总是避免同步调用