I'm looping around a JS function after an AJAX call for XML. When I use return it is undefined, even if I do if (i==x.length) {return}.
I would like the getInfo function to return the node values array. Which should be fairly simple, all of the code works apart from the return part.
var myFunction = {
sendAjax: function (url, success) {
var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP');
xhr.open('POST', url);
xhr.onload = function () {
if (xhr.readyState > 3 && xhr.status == 200) success(xhr.responseXML);
};
xhr.send();
return xhr;
},
getInfo: function () {
myFunction.sendAjax('XXXXXXXXX', function (data) {
var i;
var xmlDoc = data
var x = xmlDoc.getElementsByTagName("Session");
var sessions = [];
for (i = 0; i < x.length; i++) {
sessions[i] += xmlDoc.getElementsByTagName("XXXX")[i].childNodes[0].nodeValue + ","
console.log(sessions[i]);
}
return sessions;
});
}
}
myFunction.getInfo(function(value){
console.log('mylog=' + value);
alert(value);
});
Result comes back as undefined, it IS looping around and returning the values fine, but the return is returning before the loop is finished. I have tried doing an if statement i.e. if (i==x.length) then return ... but that does not work.
You're misunderstanding how asynchronous functions and returns interact. Execution will continue as soon as all code in the control flow has been carried out, without waiting for async callbacks to complete. Take a look at this stripped down example:
function foo(){
setTimeout(function(){
return 5;
}, 500);
}
//calling foo
alert(foo());
Note how it alerts undefined and not 5. This is because the calling code continues executing as soon as foo() is finished running, and doesn't wait for the timeout to execute. Therefore, foo() returns nothing, and alert() alerts undefined.
The correct, callback way to write the above would be:
function foo(callbackFn){
setTimeout(function(){
callbackFn(5);
}, 500);
}
foo(function(value){
alert(value);
});
Related
while (repoUrlLink != null && count < 90) {
var xmlHttp = new XMLHttpRequest();
xmlHttp.open('GET', repoUrlLink, false);
xmlHttp.setRequestHeader('Authorization', 'Bearer ' + userData.accessToken);
xmlHttp.onload = function () {
if (xmlHttp.status != 200) {
displayNoAccessMessage();
break;
}
var result = JSON.parse(xmlHttp.responseText);
if (result.length == 0) {
displayNoRecordsMessage();
break;
}
var header = xmlHttp.getResponseHeader('link');
if (header) {
//doing something
}
else
repoUrlLink = null;
$.each(result, function (index, eachData) {
// doing something with data
count++;
});
}
xmlHttp.send();
}
Is there any better way to come out of the loop as soon as i display error.The break statement is not working. Is there any callback which can be useful ?
As someone said in the comments, you'll want to use recursion. This is because xmlHttp is an asynchronous operation. When you call send, the browser will send off the request, then continue on with the code, so by the time the onload function is called, it's no longer valid to break out of the loop. Also, being a function, onload is in no position to call break, since the while loop isn't even in the same scope.
var count = 0;
var doXmlHttpCall = function() {
var xmlHttp = new XMLHttpRequest();
// additional setup code here
xmlHttp.onload = function() {
var errorFound = false;
if (/* some error case */) {
errorFound = true;
}
// process request
if (count < 90 && !errorFound) {
doXmlHttpCall();
}
}
}
doXmlHttpCall();
Some ideas to refactor the code using promises.
a promisified XMLHttpRequest request function getRepLink that performs one request. It rejects for request errors and HTTP errors (not "200" status).
a promisifed getSetOfRecords function to get a single response, parse it as JSON data and extract the link header value. It rejects iff there are no records.
a promisified process records function which tries to process a given number of records in sets. It fulfills with the number of records processed. It ignores the no records error if some records have already been processed.
// XMLHttp request
function getRepoLink (repUrlLink) {
return new Promise( function (resolve, reject) {
var xmlHttp = new XMLHttpRequest();
xmlHttp.open('GET', repoUrlLink, false);
xmlHttp.setRequestHeader('Authorization', 'Bearer ' + userData.accessToken);
xmlHttp.onload = function () {
if( xmlHttp.status == 200) {
resolve( xmlHttp);
}
else {
reject( new Error("HTTP error " + xmlHttp.status));
}
};
xmlHttp.onerror = reject;
xmlHttp.send();
});
}
// get a set of records
const EndOfRecords = new Error( "End of Records");
function getSetOfRecords( repUrlLink) {
return getRepoLink( repUrlLink).then(
function( xmlHttp) {
var result = JSON.parse(xmlHttp.responseText);
if (result.length == 0) {
displayNoRecordsMessage();
throw EndOfRecords; // reject the returned promise
}
var header = xmlHttp.getResponseHeader('link');
return {result, header}; // fulfill the returned promise
}
);
}
// get up to `count` records and process them
function processRecords( repUrlLink, count) {
var processed = 0;
function processSomeMore() {
return getSetOfRecords().then( function ({result, header}) {
$.each(result, function (index, eachData) {
// do something with data
processed++;
});
if( header) {
//do something with header
if( processed < count)
return processSomeMore() // asynchronous "recursion"
}
else {
// do as required if no link header present.
}
return processed; // fulfill returned promise
},
function( error) {
if( error === EndOfRecords && processed > 0) {
return processed; // got some records
};
throw error; // reject returned promise
});
}
return processSomeMore();
}
// Start asynchronous operation
processRecords( repUrlLink, 90)
.then( processed => console.log( processed + " records processed"))
.catch( error => console.log( error));
I have a file, tst.html with the content:
part two
(no markup or anything else, just for demonstration)
And then load said file, via synchronous AJAX (XMLHttpRequest):
function someFunc() {
var str = 'part one_';
var x = new XMLHttpRequest();
x.open('GET', 'tst.html', false); // "false" for synchronous
x.onreadystatechange = function() {
if(x.readyState === 4) {
switch(x.status) {
case 200:
str += x.responseText.trim();
break;
default:
return ''; // Or something
break;
}
}
}
x.send();
str += '_part three';
return str;
}
Calling the function:
alert(someFunc());
// Returns "part one_part two_part three"
which is the desired behavior.
But if I put the AJAX call into its own function:
function ajaxCall() {
var x = new XMLHttpRequest();
x.open('GET', 'tst.html', false);
x.onreadystatechange = function() {
if(x.readyState === 4) {
switch(x.status) {
case 200:
return x.responseText.trim();
break;
default:
return '';
break;
}
}
}
x.send();
}
function someFunc() {
var str = 'part one';
str += ajaxCall();
str += 'part three';
return str;
}
And then call it:
alert(someFunc());
// Returns "part one_undefined_part three"
The function returns the amalgamated string before the AJAX has a chance to finish, which is identical behavior to its asynchronous cousin.
I've been looking around for something along the lines of "Synchronous AJAX function," but nothing is turning up of any use.
The final use-case of the AJAX call is inside of a recursive set of functions, with further processing dependent on the AJAX return. Something like:
function one(url) {
var x = new XMLHttpRequest();
x.open('GET', url, false);
x.onreadystatechange = function() {
return two(x.responseText.trim());
}
x.send();
}
function two(str) {
var output;
output += stuff;
// ... parse through str
// ... until match found
if(isURL(match)) { // If `match` is a URL
output += one(match);
}else if(isFormattedString(match)) { // if `match` is a string
output += two(match);
}
output += more stuff;
// More processing of output
return output;
}
var final = one(url);
In the example above:
the system is always initiated with a URL (one(url))
one() returns a string, which itself is the opening argument for two(str)
Within two(), the parser can encounter either
another URL, or
a parseable string.
Depending on which it is, one of the two functions is called
The output is added to the final result of the system
A callback from one() won't work on this either, because I still need to have a final return within two().
function one(url, callback) {
// ... AJAX stuff
{
callback(two(x.responseText));
}
}
function two(str) {
// ... same as previous
// The following doesn't work, because then `two()` no longer has a `return`
// ... and any other code (i.e. for !isURL cases) will still continue to execute
if(isURL(match)) {
one(match, function(result) {
output += result;
// ... more processing
return output;
});
}else if(...) {
// ... same as previous
}
// ... more stuffs
}
The only other thing I'm finding is deferred, but I'm unsure how it would work with this either.
Is there a way to force JavaScript to treat this like other synchronous functions, where the executing code stops until the function is complete? I'm unclear why it doesn't already, with the AJAX request specifically being declared as asynchronous.
Thanks in advance.
I like your long, detailed question (wish everyone put as much effort into asking!), but in essence it boils down to this:
function ajaxCall() {
var x = new XMLHttpRequest();
x.open('GET', 'tst.html', false);
x.onreadystatechange = function() {
if(x.readyState === 4) {
switch(x.status) {
case 200:
return x.responseText.trim();
break;
default:
return '';
break;
}
}
}
x.send();
}
Your return statement is a return from onreadystatechange, not ajaxCall as you expected. It differs only from your original in that your original was just concatenating strings. It has nothing to do with having moved it to its own function.
Dont use synchronous ajax! Understand how asynchronous functions work, especially How do I return the response from an asynchronous call?
The problem is that your return
return x.responseText.trim();
returns from the handler, but not from ajaxCall function - it has no return statement, so always returns undefined.
I have problem combining javascript callbacks and revealing module pattern.
What I'm trying to do is to return the HTTP response text with the carsData.getCars() function method.
Basically what I want to do is:
return the data from xhr.onreadystatechange function to the private getData function
return the data from getData function to the public getCars function ( or call the getCars function returning a value)
I got it to work with the synchronous AJAX mode, but I'm aware it's not the best practice in javascript development.
I tried to get it to work with callbacks but failed miserably.
Is it even posible to do in javascript?
P.S. I use XMLHttpRequest in Vanilla JS instead of other frameworks for learning purposes.
'use strict';
var carsData = (function() {
var carsElement = document.getElementById('carsListing'),
successHandler = function(data) {
carsElement.innerHTML = data.data;
//return data;
},
dataWrapper = "",
getData = function(callback) {
var url = 'data/cars.json',
xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
var status;
var data;
if (xhr.readyState == 4) { // `DONE`
status = xhr.status;
if (status == 200) {
data = JSON.parse(xhr.responseText);
successHandler && successHandler(data);
callback(data);
return data;
}
}
};
xhr.open('get', url, false); // synchronous js
xhr.send();
return xhr.onreadystatechange();
//return 'xx';
}
return {
getCars: function() {
return getData(function(data){
console.log(data); // Object {data: Array[5]}
})
}
}
})();
No. You cannot do it this way. I figured out that is why you typically see results sent to a DOM object. Because they are there waiting for the answer. Your return statement, as counter-intuitive as it seems (assuming you are coming from non-prototype languages), will have already run. It seems like it wouldn't but it has because of the async nature you are aware of. You have to use Promises or you have to have your callback doing something with the data that is "waiting" for the callback data like you did with successdata.
I am overriding a javascript function like this :
(function() {
origFunc = aFunction;
aFunction = function() {
doRequest();
//return origFunc.apply(this);
};
})();
I know that I need to end the function with "return origFunc.apply(this)" in order to execute the original function. However, as I'm executing a request, I have to wait until the request is done. That's why I wrote this function :
doRequest: function()
{
try
{
if(window.XMLHttpRequest)
httpRequest = new XMLHttpRequest();
else if(window.ActiveXObject)
httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
var url = anUrl, self = this;
httpRequest.onreadystatechange = function(data)
{
try
{
if(httpRequest.readyState == 4)
{
if(httpRequest.status == 200)
return origFunc.apply(self);
else if(httpRequest.status != 0 )
alert("Error while executing the request : "+httpRequest.status+"\r\nUrl : "+url);
}
}
catch(e)
{
}
};
httpRequest.open("GET", url);
httpRequest.send();
}
catch(err)
{
alert("Error : "+err);
}
}
As you can guess, the problem is that I can't do the things like that.
Do you know how I could do ?
Here is an example for how to deal with wrapping async functions
// This simply calls the callback with some data after a second
// Could be an AJAX call for example
var doSomethingAsync = function (callback) {
setTimeout(function () {
callback({ some: 'data' });
}, 1000);
};
var fnThatMakesAsyncCall = function () {
// From the outside there is no way to change this callback
// But what if we need to intercept the async function to change the data given to it, or monitor it?
// Then we'd have to wrap the async function to wrap the callback.
var callback = function (data) {
console.log('Original', data);
};
doSomethingAsync(callback);
};
// Function to wrap another function and return the wrapper
var wrapFn = function (fn) {
// Create the wrapped function.
// Notice how it has the same signature with `callback` as the first argument
var wrapped = function (callback) {
// Here we get the original callback passed in
// We will instead wrap that too and call the original function with our new callback
var newCb = function (data) {
// This will run once the async call is complete
// We will as an example mutate the data in the return data of the callback
data.some = 'Wrapped it';
// Call the original callback with the changed data
callback.call(this, data);
};
// Run the function we wrap with the new callback we supply
fn.call(this, newCb);
};
// Return wrapped function
return wrapped;
};
// Will log Original {some: "data"}
fnThatMakesAsyncCall();
doSomethingAsync = wrapFn(doSomethingAsync);
// Will log Original {some: "Wrapped it"}
fnThatMakesAsyncCall();
I'm new to JavaScript programming. I'm now working on my Google Chrome Extension. This is the code that doesn't work... :P
I want getURLInfo function to return its JSON object, and want to put it into resp. Could someone please fix my code to get it work?
function getURLInfo(url)
{
var xhr = new XMLHttpRequest();
xhr.open
(
"GET",
"http://RESTfulAPI/info.json?url="
+ escape(url),
true
);
xhr.send();
xhr.onreadystatechange = function()
{
if (xhr.readyState == 4)
{
return JSON.parse(xhr.responseText);
}
}
}
var resp = getURLInfo("http://example.com/") // resp always returns undefined...
Thanks in advance.
You are dealing with an asynchronous function call here. Results are handled when they arrive, not when the function finishes running.
That's what callback functions are for. They are invoked when a result is available.
function get(url, callback) {
var xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
// defensive check
if (typeof callback === "function") {
// apply() sets the meaning of "this" in the callback
callback.apply(xhr);
}
}
};
xhr.send();
}
// ----------------------------------------------------------------------------
var param = "http://example.com/"; /* do NOT use escape() */
var finalUrl = "http://RESTfulAPI/info.json?url=" + encodeURIComponent(param);
// get() completes immediately...
get(finalUrl,
// ...however, this callback is invoked AFTER the response arrives
function () {
// "this" is the XHR object here!
var resp = JSON.parse(this.responseText);
// now do something with resp
alert(resp);
}
);
Notes:
escape() has been deprecated since forever. Don not use it, it does not work correctly. Use encodeURIComponent().
You could make the send() call synchronous, by setting the async parameter of open() to false. This would result in your UI freezing while the request runs, and you don't want that.
There are many libraries that have been designed to make Ajax requests easy and versatile. I suggest using one of them.
You can't do it at all for asynchronous XHR calls. You cannot make JavaScript "wait" for the HTTP response from the server; all you can do is hand the runtime system a function to call (your handler), and it will call it. However, that call will come a long time after the code that set up the XHR has finished.
All is not lost, however, as that handler function can do anything. Whatever it is that you wanted to do with a return value you can do inside the handler (or from other functions called from inside the handler).
Thus in your example, you'd change things like this:
xhr.onreadystatechange = function()
{
if (xhr.readyState == 4)
{
var resp = JSON.parse(xhr.responseText);
//
// ... whatever you need to do with "resp" ...
//
}
}
}
For small edit talking about post: https://stackoverflow.com/a/5362513/4766489
...
if (typeof callback == "function") {
//var resp = xhr.responseText;
var resp = JSON.parse(xhr.responseText);
callback(resp);
}
...
And when you call
...
function(data) {
alert(data);
/* now do something with resp */
}
...