admin管理员组

文章数量:1310263

I am starting to learn node.js. I am stuck with a problem here. I am calling a weather service which returns a JSON(url below).

.json

I want to display the results from the api on an HTML page.

I have written the following code(getInfo module) to retrieve and return the 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;
};

So to use this module I created a test.js file as follows:

var relay = require('./getInfo');
var url = '.json';
var response;
var x=relay(url);

console.log (x);

Output is as follows:

undefined                //  console.log(x) from test.js
Got response:  { version: '0.1',
  termsofService: '.html',
  features: { conditions: 1 } }

The console output in the test code runs first with no data in it. The HTTP get petes later and displays the actual output I need.

How can I modify the test code to make a blocking call such that var x in the test code actually have the JSON output instead of undefined?

Can I achieve the desired result without a blocking call to the getInfo module?

I am starting to learn node.js. I am stuck with a problem here. I am calling a weather service which returns a JSON(url below).

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

I want to display the results from the api on an HTML page.

I have written the following code(getInfo module) to retrieve and return the 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;
};

So to use this module I created a test.js file as follows:

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

console.log (x);

Output is as follows:

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

The console output in the test code runs first with no data in it. The HTTP get petes later and displays the actual output I need.

How can I modify the test code to make a blocking call such that var x in the test code actually have the JSON output instead of undefined?

Can I achieve the desired result without a blocking call to the getInfo module?

Share Improve this question edited May 27, 2014 at 2:00 Alexander Kireyev 10.8k13 gold badges67 silver badges106 bronze badges asked May 27, 2014 at 1:51 Nemil A TimbadiaNemil A Timbadia 3291 gold badge3 silver badges6 bronze badges
Add a ment  | 

3 Answers 3

Reset to default 5

As you know, node is asynchronous, so the callback of http.get and res.on('end', .. will fire after relay function is executed and it is returned. So normally you can't return the result from it.

You have a couple of choices:

  • Pass a callback to relay and use that:

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

    Then use it like this:

    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)    
    });
    
  • Use promises. This is almost same as passing callbacks. A little less lightweight, but when bining different asynchronous operations, you will understand how awesome they are. For just one asynchronous call there might not be any difference. Here I use q. You can also use bluebird which is way more performant but lacks some of the sugar of q. You can read this article to understand why promises are cleaner than callbacks in some cases.

    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);
    });
    
  • Use generators. It is part of Ecmascript 6 specification it only exists in node v0.11.x and later. But it would be almost what you want.

    With that past promise example we can do this:

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

    This is almost what you want. You can also achieve it using the callback solution with co library:

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

    You can also use node-fibers in above example which is almost a similar tool like generators.

    If you want to use bleeding edge Javascript, you can use Async/Await instead of generators with promises.

You need to pass in a callback instead:

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

Then use it like:

var relay = require('./getInfo');
var url = 'http://api.wunderground./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);
});

you can take a callback in your module function to return the result.

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

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

Then in your caller code:

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

Another option is to use httpsync module which provides synchronous apis for the same functionality that 'http' module provides. But in node js programming, you should always avoid synchronous calls.

本文标签: javascriptMake a blocking call to a function in Nodejs required in this caseStack Overflow