This question already has answers here:
Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference
(7 answers)
Closed 7 years ago.
Below is my code which make http request to a web server having host 78.154.17.70, port 8080 and path /csrftoken.json.
I use request.get to get the data from the server. I am able to fetch the data, which is Json data in the following format:
{
"apiVersion": "1.0",
"data": {
"csrf": "ajbgajbgjabbjbjbab"
}
}
I declared two variables var CSRFTokenValue; and var respJson; globally. Inside request.get, I use these variables as: respJson store the parse data and CSRFTokenValue store the
"csrf": "ajbgajbgjabbjbjbab" token value as "ajbgajbgjabbjbjbab"
means CSRFTokenValue will store "ajbgajbgjabbjbjbab" in it.
Now when i log CSRFTokenValue inside the request.get it will give me the "ajbgajbgjabbjbjbab" but when I log it outside it will give me undefined .
I need CSRFTokenValue to use in the quesryString inside the request.post to post the data.
I don't know how to get the tokenValue globally??
var request = require('request');
var CSRFTokenValue;
var respJson;
request.get('http://78.154.17.70:8080/csrftoken.json', function (e, res, body){
respJson = JSON.parse(body);
CSRFTokenValue = respJson.data.csrf;
console.log('GET success:' + CSRFTokenValue);
});
console.log('GET token Globaly:' + CSRFTokenValue);
request.post('http://78.154.17.70:8080/Login/post.json?_csrf=' + CSRFTokenValue, {
'name' : 'name',
'password' : 'pass'
}, function (res) {
console.log('success');
});
This is because when you make your initial request it is being done asynchronously. The program moves right to the next statement in your control flow while your request is done in the background. When the request finishes, CSRFTokenValue will be set but you're printing out the value before the request has finished so it's undefined.
Try this instead.
var request = require('request');
request.get('http://78.154.17.70:8080/csrftoken.json', function (e, res, body){
var respJson = JSON.parse(body);
var CSRFTokenValue = respJson.data.csrf;
console.log('GET success:' + CSRFTokenValue);
request.post('http://78.154.17.70:8080/Login/post.json?_csrf=' + CSRFTokenValue, {
'name' : 'name',
'password' : 'pass'
}, function (res) {
console.log('success');
});
});
This will make your second request once you've gotten the token needed from your first one.
Also, take a look into asynchronous programming.
The line,
console.log('GET token Globaly:' + CSRFTokenValue);, would execute immediately, bypassing your asynchronous call, request.get('http://78.154.17.70:8080/csrftoken.json'.... That's the reason CSRFTokenValue is undefined, as in the declaration, you did not initiate it with a value.
Use a promise to run your request.post('http://78.154.17.70:8080/Login/post.json?_csrf='.... Simply run that within request.get when the AJAX call is successful.
Related
I am new to Node.js and I have created a method that should asynchronously fetch jsonp data via ajax and display the retrieved content in a graph. This method works fine when the url points to a static js file (in this case productsData.js) containing jsonp data:
function loadChart(destElementId, alertId) {
$.ajax({
url:'http://localhost:3000/products/data/productsData.js',
type: "GET",
data: {prodId: prodId},
jsonp: true,
dataType : 'json',
jsonpCallback: "jsonpCallback"
});
window["jsonpCallback"] = function(data) {
populateData(data, destId);
}
}
As in normal applications, I want to pass real data fetched via an external web service. I have created the following js file (data-client.js) that retrieves data from a specific web service. When calls are browser-based, the data is fetched successfully as a normal json and is displayed accordingly in the browser.
var express = require('express');
var router = express.Router();
var http = require('http');
var yaml_config = require('node-yaml-config');
var config = yaml_config.load(__dirname + '/../config/app-config.yml');
router.get('/data/:id', function (req, res, next) {
var opts = {
host: config.alertService.host,
port: config.alertService.port,
method: 'GET',
path: '/DataService/rest/receiveData/' + req.params.id
}
var reqGet = http.request(opts, function (dataResponse) {
var responseString = '';
dataResponse.on('data', function (data) {
responseString += data;
});
var response = {x:[],y:[],z:[],t:[]};
dataResponse.on('end', function () {
var responseObject = JSON.parse(responseString);
var accs = responseObject.data.listPCS;
for(var i in accs){
response.x.push(accs[i].accX);
response.z.push(accs[i].accY);
response.y.push(accs[i].accZ);
response.t.push(accs[i].timestamp);
}
res.json(response);
});
});
reqGet.end();
reqGet.on('error', function (e) {
console.error(e);
});
});
module.exports = router;
The first step for using live jsonp data is to replace the previous url value with:
url: 'http://localhost:3000/products/data/'+productId,
The second step is to replace in the data-client.js:
res.json(response);
with:
res.jsonp('jsonpCallback('+ JSON.stringify(response) + ');');
Somehow the data is not fetched. When I try to get the data via the browser (i.e. by entering http://localhost:3000/data/ID937) however I get the following result:
"jsonpCallback({\"x\":[1,1,1],\"y\":[2,1,4],\"z\":[0,0,9],\"t\":[1462790772000,1462790772010,1462790772020]});"
Can someone please tell me where the problem may be? I would be very thankful.
It looks like you are using Express.
jsonp() expects to be passed your raw data, not a string containing the complete response. It also expects to read the callback name from the query string, which jQuery will generate for you if you let it.
So the first thing to do is to fix your client side code so it passes the callback name correctly
On the client
$.ajax({
url: 'http://localhost:3000/products/data/' + productId,
dataType: 'jsonp',
}).done(function(data) {
populateData(data, destId);
});
The changes here are:
GET is the default (and, for JSONP, only option) so there is no need to specify the type.
You are passing the data as a URL path part, not a query string. Drop the data property that you aren't using.
The jsonp property is used to override the callback name. That's harmful, don't do that (likewise, don't specify jsonpCallback). jQuery will generate one for you and add it to the query string.
If you are dealing in JSONP then specify that as the data type, not JSON.
Don't create your own global manually. Pass your function to done and let jQuery make it a global (with a generated name to match the callback).
On the server
Just pass the data structure you want to send back to jsonp().
res.jsonp(response);
The callback name will be read from the query string by Express.
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Why isn't my future value available now? [duplicate]
Closed 6 years ago.
Forgive me, but as an extremely new developer I fail to see how this is an exact duplicate of the questions referred to. I have no idea what ajax is, and I certainly don't understand how to adapt the proposed solutions to work in my situation.
I'm writing a very simple node app that gets a stock price from a web api, get a currency conversion value from another web api, and converts the stock price from USD to AUD. I had this working perfectly in a single file, but I've chosen to split it out in to several modules to make it easier. And now I've broken it :)
Basically I pass the ticker I wish to lookup into my GetStockPrice() function, which then perform the necessary query and returns the result.....in theory.
This is how I call my module:
var returned = GetStockPrice('AMZN');
console.log(returned);
The returned value is always 10, as it's not being updated in my GetStockPrice module.
Here is the code in my module:
var http = require('http');
var stockPrice = 10;
function GetStockPrice(ticker) {
var options = {
host: 'dev.markitondemand.com',
port: 80,
path: '/MODApis/Api/v2/Quote/json?symbol=' + ticker,
method: 'GET'
};
http.request(options, function(res) {
console.log('STATUS: ' + res.statusCode);
res.setEncoding('utf8');
res.on('data', function(chunk) {
const json = JSON.parse(chunk);
stockPrice = json.LastPrice;
});
}).end();
return (stockPrice)
};
module.exports = GetStockPrice;
I understand what I'm doing wrong, the http.request is being run asynchronously, so by the time my function gets to the return statement, the async call hasn't completed, so my return value, stockPrice, is still the same as when I first initialized it. Unfortunately, I don't know how to fix it. I tried moving the return statement inside the http.request function, but that doesn't work either.
Can anyone help please?
You can use callback to get response from asynchronous functions.
var http = require('http');
var stockPrice = 10;
function GetStockPrice(ticker, callback){
var options = {
host: 'dev.markitondemand.com',
port: 80,
path: '/MODApis/Api/v2/Quote/json?symbol=' + ticker,
method: 'GET'
};
http.request(options, function(res) {
console.log('STATUS: ' + res.statusCode);
res.setEncoding('utf8');
var stockPrice='';
res.on('data', function (chunk) {
stockPrice = stockPrice + chunk;
});
res.on('end', function(){
return callback(JSON.parse(stockPrice));
});
});
};
module.exports = GetStockPrice;
And
GetStockPrice('AMZN', function(data){
console.log(data);
});
Pardon me, if there are any typos.
I'm trying to process the returned JSON result from the request
so I need to expand its scope outside this request call.
To do that I declared data variable with an empty string and assign the result to this data but it doesn't print the result.
How can I accomplish this?
module.exports = function(callback) {
var request = require("request")
var url = "http://sheetsu.com/apis/94dc0db4"
var data = "";
request({
url: url,
json: true
}, function (error, response, body) {
if (!error && response.statusCode === 200) {
callback(body)
data = body;
}
})
console.log(data);
}
This is classic async confusion : your console.log call will happen before the http request callback.
Your script is executed in this order:
request() is executed
console.log(data)
request() callback function, where you asign data a value
If you want to print data, you must do it inside the request callback function. The async module, is very useful when performing async tasks, specially if you need to perform tasks in a specific order and use the data from this requests.
I'm doing a JSON call like this:
var desc = getItemDescriptions(xxx);
function getItemDescriptions(xxx) {
var url = "xxx;
var info = {};
request({
url: url,
json: true
}, function (error, response, body) {
if (!error && response.statusCode === 200) {
info ["..."] = body.result[xxx].yyy;
info ["..."] = body.result[xxx].yyy;
info ["..."] = body.result[xxx].yyy;
info ["..."] = body.result[xxx].yyy;
info ["..."] = body.result[xxx].yyy;
}
})
return info;
}
My Problem is, the JSON request need some time to get response back... and my function doesn't wait for this response. The Function returns the empty array without waiting.
How can i wait for response and then return the filled array?
thx
Its not like executing fast, it is the way javascript runs statement after statement. To get the data you need to do that in success callback function, the data would be available only when the server response comes back as its asynchronous call by the time response comes your javascript executes next statements.
Juhana already linked to you the best place to get a good solution. How to return the response from an async call
A quick and dirty hack would be (if request is a jQuery-like Ajax-function) to make the request synchronus.
This might be done by adding async: false to the first parameter passed to request:
request({ url: url,json: true, async: false}, function ....
That way return info will be executed AFTER your request has finished.
HTH
Georg
I'm using express and request to turn a site's html into json, then returning it. For example:
app.get('/live', function(req,_res){
res = _res;
options.url = 'http://targetsite.com';
request(options,parseLive);
});
function parseLive(err, resp, html) {
var ret = {status:'ok'};
-- error checking and parsing of html --
res.send(ret);
}
Currently I'm using a global var res to keep track of the return call, but this fails when multiple requests are made at the same time. So, I need some way of matching return calls from express to their callbacks in request.
How might I do this?
Use a closure.
Pass the variable to a function. Return the function you want to pass to request from that function.
app.get('/live', function(req,_res){
options.url = 'http://targetsite.com';
request(options,parseLiveFactory(res));
});
function parseLiveFactory(res) {
function parseLive(err, resp, html) {
var ret = {status:'ok'};
-- error checking and parsing of html --
res.send(ret);
}
return parseLive;
}