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;
}
Related
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 5 years ago.
I have looked and looked and I am still scratching my head. If I have missed something obvious, I apologise. I have tried to create a custom library of functions that I have written myself (thanks stackoverflow for helping me work that one out....). I then have a javascript file that loads when the web page is called, which in turn calls said functions in my custom library.
I have a function called getConfig() that does the obvious. It gets a JSON file with the configuration details for my server that hosts all of my RESTful web services. When I step through the code, the configuration details are returning as I would expect, however, when I load the web page at full speed, the configuration object comes back as undefined. I thought it might be a timing thing, so I wrapped everything in a $(document).ready(function()) block, but no go. I even tried a window.onload = function(){} block to make sure everything is loaded before the custom libraries are called. No luck! Its doing my head in as I cannot for the life of me work out what is going on.
My custom library file looks like this with filename xtendLibs.js
var xtendLibs = {
getConfig : function(){
var CONFIG;
$.getJSON("/js/config.json", function(json){
CONFIG = json;
});
return CONFIG;
},
getObjects : function(config, medicareno, medicarelineno, objectType){
var object;
var urlString = config.scheme + config.muleHost + config.mulePort + ":/patients/";
switch(objectType){
case ("details") :
urlString = urlString + "details/" + medicareno + "/" + medicarelineno ;
break;
case ("appointments") :
urlString = urlString + "appointments/" + medicareno +"/" + medicarelineno;
break;
}
$.ajax({
type : 'GET',
url : urlString,
success : function(data){
object = data;
},
failure : function(){
alert("Failure");
}
});
return object;
},
getUrlParameters : function(){
var paramsArray = window.location.search.substring(1).split("&");
var obj = [];
var tempArray;
var paramName,paramValue;
for(var i = 0; i < paramsArray.length; i++){
tempArray = paramsArray[i].split("=");
paramName = tempArray[0];
paramValue = tempArray[1];
obj[paramName] = paramValue;
}
return obj;
}
};
The javascript file that calls the various functions in the above file looks like this appts.js
window.onload = function(){
var config, params, appointments;
params = xtendLibs.getUrlParameters(); // This line works - and params is returned
config = xtendLibs.getConfig(); // This line fails but will work if I step through the code
appointments = xtendLibs.getObjects( config,
params["medicareno"],
params["medicarelineno"],
"appointments");
console.log(params);
}
I am truly stumped. Any help would be greatly appreciated.
Ajax is async process, so when getJson is called it does not stop the execution of next statement.
getConfig : function(){
var CONFIG;
$.getJSON("/js/config.json", function(json){
CONFIG = json;
});
return CONFIG;
}
When getJson is called it switches to a new thread, and the next statement which is in this case is "return CONFIG;" is executed. However, CONFIG has not been defined yet, so it is returning as undefined.
How Could you solve this problem?
You could not solve this problem. Not using this code design. You could non async the ajax, but it will make your page freeze.
You could set a global variable "config" when "getConfig" is called and check whether the config variable is defined when executing any function concerning it, but the best approach would be to pass a function, containing all the statements to be executed when config has finished loading, in getConfig function and call it when "/js/config.json" has loaded.
Say if I have small function that takes a Request object as an argument, and calls the fetch() API.
Except I always want to append something to the url, such as ?foo=bar. I'm curious what the best way would be to go about that.
Example:
function fetchFoo(request) {
request.url += '?foo=bar';
return fetch(request);
}
The issue I have is that this won't work. The Fetch API specification states that the url property read-only.
Is there a way to work around this? I'm thinking I might need to construct an all-new Request object, but I'm unsure what a clever way is to inherit all the options from the previous Request object.
Note that I am able to override any other property by using this syntax:
var originalRequest = new Request('/url');
var overriddenRequest = new Request(originalRequest, { method: 'POST' });
Although it wasn't clear from the docs, it seems that the second init parameter takes precedence over the values passed via the originalRequest parameter. I just can't seem to come up with a way to do this for the url as well.
You could leverage the keys that are on the Request.prototype to build a new Request object in just a few lines.
function newRequest(input, init) {
var url = input;
if (input instanceof Request) {
url = mutateUrl(input.url);
init = init || {};
Object.keys(Request.prototype).forEach(function (value) {
init[value] = input[value];
});
delete init.url;
return input.blob().then(function (blob) {
if (input.method.toUpperCase() !== 'HEAD' && input.method.toUpperCase() !== 'GET' && blob.size > 0) {
init.body = blob;
}
return new Request(url, init);
})
} else {
url = mutateUrl(url);
}
return new Request(url, init);
}
Note the special case for body discussed in this answer.
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.
Here's my javascript file
var callAjax = function(relative_path){
var Ajax = new XMLHttpRequest();
Ajax.onreadystatechange = function() {
//Since what we are calling a local file. we cannot get a 200 OK Status.
//So We check only the readystate
if(Ajax.readyState==4){
serialized = Ajax.responseText;
alert(serialized);
// ^^ alerts fine.
return serialized;
}
}
Ajax.open("GET",relative_path, true);
Ajax.send();
};
var readSettings = function(){
var data = callAjax('settings.json');
obj = JSON.parse(data);
alert(obj);
}
Now when i call readSettings() somewhere in my html, the first alert (in the callAjax functions alerts the JSON correctly. but the second one does not. When i see the console, the error is :
[21:04:02.233] SyntaxError: JSON.parse: unexpected character # file:///home/cipher/Codes/looma-f5/js/looma.js:23
My settings.json is:
{
"classes": 8,
"config": "classConfig",
"locale": {
"en": "localeEn"
},
"defaultLocale": "en"
}
I ran the JSON through online tools, it looks good. Why is firefox not parsing these?
You're not returning any value from the callAjax function. You need to put your code that uses the result inside the onreadystatchange handler.
You do have a return statement, but it's inside the callback, which returns to the internal caller of the callback, and which is invoked after your callAjax returned.
Since it seems that callAjax is fairly generic, a good approach is to have that function accept a callback as an argument, then invoke it, passing in the response.
// receive a callback----vv
var callAjax = function(relative_path, callback){
var Ajax = new XMLHttpRequest();
Ajax.onreadystatechange = function() {
if(Ajax.readyState==4){
serialized = Ajax.responseText;
alert(serialized);
// vv---invoke the callback
callback(serialized);
}
}
Ajax.open("GET",relative_path, true);
Ajax.send();
};
var readSettings = function(){
// pass a callback ----------------------vv
var data = callAjax('settings.json', function(data) {
var obj = JSON.parse(data);
alert(obj);
});
}
If the caller of readSettings needs to work with the response, then you could have readSettings also receive a callback, and then either pass it on directly, or wrap it in another function so that it can first do the parsing.
I'll give an example that assumes that it needs to be wrapped.
// receive a callback----------vv
var readSettings = function(callback){
// pass a callback that wraps the first---vv
var data = callAjax('settings.json', function(data) {
// parse the response data
var obj = JSON.parse(data);
// invoke the callback, passing it the parsed object
callback(obj);
});
}
readSettings(function(settings_obj) {
alert(settings_obj);
});
The problem is that you can JSON.parse immediately after callAjax returns. Before the onreadystate callback is executed. Due to asynchronous nature of the operation you should trigger parsing from the callback.
The "A" in AJAX is for asynchronous. After submitting the XMLHttpRequest, the callAjax function will return immediately, without waiting for the request to complete. Thus, the JSON.parse will be called with the return value of callAjax (which isn't the JSON you're looking for), generating the error.
Some time later, the XMLHttpRequest will complete and the onreadystatechange callback will be run. You return the JSON from this function, but the JSON doesn't go anywhere because it's returning from the callback, not from callAjax.
You must perform the JSON parsing and subsequent activities as a result of the onreadystatechange event.
I would like to call a function within a function and have this inner function return an object. I am using a JSONRequest function that I created myself so just assume that the request fetches an array of roles. Here is the code:
(users = function(){
this.getUserRoles = function(){
var params = {};
var json = new JSONRequest(function(data){
rolesObj = data['roles'];
return rolesObj;
}, 'get_roles', params);
}
});
Then I call the following but it returns undefined:
var cls = new users();
alert(cls.getUserRoles().length);
ajax requests are asynchronous so you're not going to get a return value from them. You do, however, receive a callback when they complete so you can pass your "return" value into a callback.
Here is an example of your code re-written to use a callback:
this.getUserRoles = function(completionCallback){
var params = {};
var json = new JSONRequest(function(data){
rolesObj = data['roles'];
completionCallback(rolesObj);
return rolesObj;
}, 'get_roles', params);
}
and then using it:
var cls = new users();
cls.getUserRoles(function(roles) {
alert(roles.length);
});
The rolesObj is passed into the callback function once the JSON request completes.
Your getUserRoles function doesn't return anything. It invokes an asynchronous JSON request, and the success callback for that request returns something.
There is no way to make an asynchronous request in a function and have the function synchronously return a value from the request. The request will take an unknown amount of time, and the requesting function will return as soon as the request has been sent. The value won't have arrived back from the server yet.