Have a following node.js call to http get REST API. When I run it, it returns the JSON result. However, I am trying to get the result to console.log on var r after assigning the result. Why do I always get undefined? Looks like I am messing up the javascript scope. Any help is appreciated.
var http = require('http'),
oex_appId = 'myappId',
_hostname = 'openexchangerates.org',
_path = '/api/latest.json?app_id=' + oex_appId;
var r;
function getRates(cb) {
var options = {
hostname: _hostname,
path: _path,
method: 'GET'
};
var responseCallback = function(response) {
var latestRates = '';
var parseCurrencies = function(rates) {
var currencies = JSON.parse(rates);
currencies.rates['USD'] = 1;
return currencies.rates;
};
response.setEncoding('utf8');
response.on('data', function(chunk) {
latestRates += chunk;
}).on('error', function(err) {
throw new Error(err);
}).on('end', function() {
cb(parseCurrencies(latestRates));
});
};
var req = http.request(options, responseCallback);
req.on('error', function(e) {
throw e;
});
req.end();
};
var d = function(data) {
console.log(data["INR"]);
currencies["INR"].amount = data["INR"];
r = data;
};
getRates(d);
console.log(r);
Why do I always get undefined?
Your problem is not an scope issue but a misunderstanding of async execution. In this code you are printing the value of r before it is assigned.
The execution of your code is as follow:
getRates() is called
console.log(r) is called (hence you get undefined)
When the request executed in getRates finishes your callback is THEN executed. This is when you are assigning a value to r but by then is too late since you already printed it.
In general, you cannot expect the next line to have a value that will be assigned via an async callback. Instead you should reference the value inside the callback. In your case you should move the console.log(r) inside your callback function d.
Related
I am trying to pass data out of my get request from resp.on function. I want to use 'var url' to make a separate get request from which I will parse data again. I am able to console.log variables from inside the function but not return (or access from outside). This seems to be a scoping or async issue.
const https = require('https');
https.get('https://collectionapi.metmuseum.org/public/collection/v1/objects', (resp) => {
let data = '';
// A chunk of data has been recieved.
resp.on('data', (chunk) => {
data += chunk;
});
// The whole response has been received. Print out the result.
resp.on('end', () => {
var json_data = JSON.parse(data);
var total = json_data.total
var random_objectID = Math.floor(Math.random()*total)
var url = 'https://collectionapi.metmuseum.org/public/collection/v1/objects/' + random_objectID
console.log(url);
});
}).on("error", (err) => {
console.log("Error: " + err.message);
})
//'url' becomes unknown here. I want to pass it to another get request.
It's both an async and a scope issue!
If you declare var url; in the outermost scope you'll be able to set it inside that callback as intended, but since that's happening asynchronously you won't be able to use the value outside the scope unless you are checking after the callback completes.
Alternatively, you could wrap the whole thing in a promise, e.g.
const https = require('https');
new Promise((resolve,reject)=>{
let targetUrl = 'https://collectionapi.metmuseum.org/public/collection/v1/objects';
https.get(targetUrl,resp=>{
// ...
resp.on('end', () => {
// ...
resolve(url);
});
});
}).then(url=>{
// do stuff with that URL
});
If your goal is to automate fetching data from web resources, I'd recommend checking out the request module, which also has a promisified variant.
I want to use result of unirest request to another file in Node.js but I can not get data from request.end() function to outer variable.
Code is below:
request.end(function (response) {
if(response.error) {
console.log("AUTHENTICATION ERROR: ", response.error);
} else {
callback(null, response.body);
}
console.log("AUTHENTICATION BODY: ", response.body);
});
var result = authentication(function(error, response) {
var authenticationToken = response.access_token;
if(authenticationToken != null) {
console.log("Token: ", authenticationToken);
return authenticationToken;
}
});
I want to get authenticationToken value to export with module.exports for another modules.
I am using unirest http library.
its a callback function, and is treated as parameter, not a function that returns value.
You can do this though:
var result;
authentication(function(error, response) {
var authenticationToken = response.access_token;
if(authenticationToken != null) {
console.log("Token: ", authenticationToken);
module.exports.result = authenticationToken; // setting value of result, instead of passing it back
}
});
you can use result variable now.
But be careful, its an asynchronous function, so you may not be able to use it immediately, until a value is assigned to it, within a callback function.
To export result value:
module.exports.result = null;
Example
m1.js
setTimeout(() => {
module.exports.result = 0;
}, 0);
module.exports.result = null;
app.js
const m1 = require('./m1.js');
console.log(JSON.stringify(m1));
setTimeout(() => {
console.log(JSON.stringify(m1));
}, 10);
Output
{"result":null}
{"result":0}
so you can keep using variable result, once the variable is assigned it would contain the value.
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 5 years ago.
I am trying to build a simple web app that pulls API data on the price of Bitcoin. I am able to parse the data and extract the price. However, given the asynchronous nature of Node.js, when I try to return the data it returns before the http request is complete.
This is why I inserted the callback function. However, when it returns the price it is still undefined. Hence, I assume that the request is not complete when the return is sent even with the callback?
I have also heard of other solutions such as promises and async yet I cannot find a way to return the price variable from the function. I need to be able to do this so that I can do separate calculations on the returned variable.
Disclaimer: I am a node newbie so any help would be appreciated
var https = require('https');
function myCallback(result) {
return result;
}
var price = 0;
function getData(callback) {
var url = "https://api.coinbase.com/v2/prices/spot?currency=USD";
https.get(url, function(res){
var body = '';
res.on('data', function(chunk){
body += chunk;
});
res.on('end', function(){
var exData = JSON.parse(body);
price = Number((exData.data.amount))*14.5;
callback(price);
});
}).on('error', function(e){
console.log("Got an Error: ", e);
});
};
var y = getData(myCallback);
console.log(y);
The hard thing when you start in javascript is start thinking in asynchronous operations. When console.log(y) is executed the HTTPS call isn't completed yet so it can't be called that way once majorly IO functions in node are non-blocking. What you need to do is to call console.log inside the myCallback function.
var https = require('https');
function myCallback(result) {
console.log(result);
}
var price = 0;
function getData(callback) {
var url = "https://api.coinbase.com/v2/prices/spot?currency=USD";
https.get(url, function(res){
var body = '';
res.on('data', function(chunk){
body += chunk;
});
res.on('end', function(){
var exData = JSON.parse(body);
price = Number((exData.data.amount))*14.5;
callback(price);
});
}).on('error', function(e){
console.log("Got an Error: ", e);
});
}
getData(myCallback);
EDIT:
The thing is, you can set the value of a variable outside the callback by you still have to ensure that you'll be using this variable only after the attribution. You still have to use callbacks one way or another.
There's another way of pass callbacks around and it has a nicer way for doing that, promises. I believe that is what you're looking for.
var https = require('https');
var price = 0;
function getData() {
var promise = new Promise(function(resolve, reject) {
var url = "https://api.coinbase.com/v2/prices/spot?currency=USD";
https.get(url, function(res){
var body = '';
res.on('data', function(chunk){
body += chunk;
});
res.on('end', function(){
var exData = JSON.parse(body);
price = Number((exData.data.amount))*14.5;
resolve(price);
});
}).on('error', function(e){
console.log("Got an Error: ", e);
reject(e);
});
})
return promise;
}
var y = getData()
y.then(function (result) {
console.log('price: ' + result);
});
I have a problem when I call my function JavaScript I can not return my Buffer because it's in other function
index.js:
var client = require('./sender');
//envoyer le paquet au seveur blizzard.com
var received =client.sendAndreceive(bufenc);
console.log('received: '+received);
sender.js:
var Http = require('http');
var Url = require('url');
function sendAndreceive(data) {
console.log('##########################################################################');
// connexion au serveur
var options = {
//proxy
host: "proxya.u-pec.fr",
port: 3128,
//hostname : 'http://m.eu.mobileservice.blizzard.com',
//port : 80,
path : 'http://m.eu.mobileservice.blizzard.com/enrollment/enroll.htm',
method : 'POST',
headers: {
Host: "http://m.eu.mobileservice.blizzard.com"
}
};
var req = Http.request(options, callback );
req.on('error', function(e) {
console.log('problem with request: ' + e.message);
});
req.write(data);
req.end();
}
exports.sendAndreceive = sendAndreceive;
function callback (res) {
res.on('data', function(chunk) {
buf = new Buffer(chunk);
console.log('data length: ' + buf.length);
return buf; // my problem is her !!!!!!!!!!!!!!!!!!!!!!
});
}
output:
##########################################################################
received: undefined
data length: 45
Using the JavaScript callback pattern
You won't get a result as you returning the result from the callback function, which is nothing and wouldn't work either as your request to the server is asynchronously. Take a look at the example below, and see how it can work:
function callback (res, callback) {
res.on('data', function(chunk) {
buf = new Buffer(chunk);
console.log('data length: ' + buf.length);
// Make sure the argument is indeed a callable function
if (typeof callback === 'function') {
callback(buf);
}
});
}
Now what you do is simply implement a callback, that is executed when the result is finished.
var client = require('./sender');
//envoyer le paquet au seveur blizzard.com
client.sendAndreceive(bufenc, function(received) {
console.log('Received', received);
});
This JavaScript pattern is called the callback pattern, and can be very useful in these kinds of situations.
Using a promise
Another more sexy way of doing stuff like this, is through promises. I don't want to fully explain what a promise is or does (you can use search, right?), so I'll just show you an example:
// This is part of the node-promise module: https://github.com/kriszyp/node-promise
var deferred = require("promise").defer;
var client = require('./sender');
function callback (res) {
// Create a deffered object that will be returned
var deferred = defer();
res.on('data', function(chunk) {
buf = new Buffer(chunk);
console.log('data length: ' + buf.length);
// Now let the listeners know that this promise
// has been completed successfully, and return the buf
deffered.resolve(buf);
// Or if something went wrong, you could do the following:
// deffered.reject('Everything is wrong!');
});
return deferred;
}
//envoyer le paquet au seveur blizzard.com
client.sendAndreceive(bufenc)
.then(
// Function ended successfully
function(received) {
console.log('Received', received);
},
// Function returned an error
function(err) {
console.log('Oh noes!', err);
}
)
;
You are calling sendAndreceive(), and sendAndreceive() is starting a HTTP request that asynchronously calls callback(). So the return value of callback() is a return value given to the invoker inside the HTTP request object, while sendAndreceive() returns nothing.
There is no way to force asynchronous calls behave like one step-by-step-flow. Therefore I support the recommendation of Boy, using a callback.
OK, I see the promise pattern by Boy :-) cool. I will take a deeper look. It seems you can make asynchronous steps at least looking like synchronously written with constructs like .then(). Thx!
I think I am confusing about the scope of JavaScript. When I tried following code (using Node.js), I could not get any output from second "console.log()". In this program, I think value variable should have some value after executing http.get() since value variable is in global scope. But returned value is against my anticipation.
var http = require('http');
var options = {
host: "www.example.com",
path: "/index.html",
port: 80
};
var value = "";
var ret = http.get(options, function(res) {
res.on("data", function(chunk) {
value += chunk;
});
res.on("end", function(chunk) {
console.log(value); // have some value...
})
});
console.log(value); // don't have any value...why?
I would like to know what is going on. Thanks in advance!
(following text is a supplement)
Thanks a lot to three guys who gave me answer! I delayed the execution of second console.log() using setTimeout as follows. But I still have the same problem. I wonder how I can get value.
var http = require('http');
var options = {
host: "www.example.com",
path: "/index.html",
port: 80
};
var value = "";
var ret = http.get(options, function(res) {
res.on("data", function(chunk) {
value += chunk;
});
res.on("end", function(chunk) {
console.log("first: " + value); // have some value...
})
});
setTimeout(function() {
console.log("second: " + value); // don't have any value...why?
}, 5000);
You're defining a callback function on the http.get, and then you're executing through, so executing the final console.log, BEFORE value is set. In the meantime, I don't know if your callback is actually getting called. Try adding some additional console.log statements to make sure your flow of execution is what you expect.
The key here is asynchronous callbacks.
What happens is the function(res) {...} is called and when http.get is finished and there it will add values to value. However, after http.get it will log value when it's not been filled yet.
So basically,
http.get is called
console.log is called
http.get is finished, the function(res) {...} is called
An http request is asynchronous. The second console.log() (where the variable value has no value) is actually called before the request returns, and therefore, it has the same value it was initialized with.