Using this keyword in XMLHttpRequest callback - javascript

What is difference between using this keyword vs variable name in XMLHttpRequest callback?
var req = new XMLHttpRequest();
req.open("GET", encodeURI(uri), true);
req.onreadystatechange = function () {
if (this.readyState == 4) {
req.onreadystatechange = null; //avoid memory leaks
if (this.status == 200) {
var res = JSON.parse(req.responseText).d;
console.log(res);
}
}
};
req.send();
Can I just use req instead of this, like if (req.readyState == 4)?

There is no difference in onreadystatechange handler by default this refers to the XMLHttpRequest object itself.
So yes, you can use either this or req

I myself have encountered this similar problem recently, but I extended it to more general case: which context does this resolved to in callback?
regard to 3 prevalent callback type: setTimeout, XMLHttpRequest, DOM
I read from the doc for first type, setTimeout, this will resolved to window object
for XMLHttpRequest situation, this will resolved to XMLHttpRequest object itself, https://stackoverflow.com/a/6542261/4730119 this link will give you a explain and here is my test, for which I GET requested api.json's content is a plain simple object and successfully printed in console, notice I used this.responseText, not like most book taught using XHR object request.responseText
and last for DOM, it's just like XHR, this will resolved to object for which the callback added
document.querySelector('input')
.addEventListener('input', function(e){console.log(this)});
// will print input object

Related

AJAX onreadystatechange function not always called

I have a AJAX call to retrieve some XML using the below method. Often when I run the code it does not enter the onreadystatechange function until the last iterations of my foreach loop. I'm assuming this is because the calls to "www.webpage.com/" + arrayValue are taking enough time that before the state is updated to "Ready" and then the next request beings. The method probably only runs for the last iteration of the loop because there is no other request to override it and thus has time to become "Ready". From what I've seen online you can't really do a tradition Wait() statement in javascipt or AJAX to give the calls time to complete. So how can I overcome this issue?
var getXML = new XMLHttpRequest();
myArray.forEach((arrayValue, index) => {
getXML.open("GET", "www.webpage.com/" + arrayValue, true);
getXML.setRequestHeader('Access-Control-Allow-Credentials', true);
getXML.setRequestHeader('Authorization', "Basic " + btoa(":something"));
getXML.send(null);
getXML.onreadystatechange = function () {
if(this.readyState == this.DONE) {
Console.Log("We made it in!");
}
}
});
The problem here is that you are trying to use the same XMLHttpRequest object for multiple requests.
Don't do that. Create a new, clean instance of XMLHttpRequest for each request.
myArray.forEach((arrayValue, index) => {
var getXML = new XMLHttpRequest();
getXML.open("GET", "www.webpage.com/" + arrayValue, true);

Converting a function that loads data from local dataspace to asynchronous operation

I have an old function that is no longer functional. According to the console window, synchronous mode is no longer permitted for use. How would I convert this to use asynchronous mode and deliver the data out?
var loadfile = function (filename, filetype) // Reads a file and returns it's contents as a string.
{filetype = filetype || 'text' // Assume text if no filetype is passed in
var reader = new XMLHttpRequest() || new ActiveXObject("Microsoft.XMLHTTP"); // Provide a fallback for IE
reader.responseType = filetype; // Prepare to read the proper type of data
reader.open("GET", filename, false); // Target our local filename and prepare for synchronous read
reader.send(); // Begin the read
return reader.responseText; // Return the data as expected
};
I know I could force this to operate by removing line 4, but then I get XML Errors and warnings about the depreciated synchronous mode being employed. Warnings that could become 'no longer supported' errors that would block the program run months from now.
Also, I can switch to asynchronous mode by changing line 5's false to true, but then there's no data being passed out as the last line is invoked immediately. I could involve reader.onloadend() to process the data, but a return invoked there just casts the read data into the void when I need it to be passed back to the caller of loadfile().
Ergo, I'm stuck. What am I missing here?
EDIT: Adding a potential async version here and pointing out how it doesn't work.
var loadfile = function (filename, filetype)
{var output = '';
filetype = filetype || 'text';
var reader = new XMLHttpRequest() || new ActiveXObject("Microsoft.XMLHTTP");
reader.responseType = 'text';
reader.open('GET', filename);
reader.onloadend = function () {output = reader.responseText;}
reader.send();
while (output === '') {}
return output;
};
Naturally this has several issues. First, the scope of reader.onloadend() makes it's copy of output separate from the one in loadfile(). So the retrieved data just disappears. We could return it instead of assigning it to a local variable, but onloadend fires itself (as events do) and the returned data is sent into the void. Unrecoverable. Second, even if we could change loadfile()'s version of output from within the onloadend() function as per passing by reference in C++, the while loop that waits for the output variable to change would (because JavaScript is a single process thread) lock up the system, running an infinite loop and not allowing any changes to occur.
Ergo, still stuck. Yes, we could output to window.name or console.log or document.write, but none of these options allows loadfile() to return the data acquired from XHMLHttpRequest/filename.
At this point I'm stuck with the depreciated synchronous xhr, and XML Parsing Errors as I am retrieving raw text, not XML.
EDIT SECUNDUS: I finessed the script to default to synchronous mode, and managed to silence the errors (but not the initial warning about using a depreciated method...thankfully that doesn't spam warnings on every usage). Needless to say using async/true on this function will not get you anywhere, but async/false does work....for now. Sharing in case anyone else needs this particular functionality.
var loadfile = function (filename, async)
{if ("undefined" === typeof(async)) {async = false;}
var reader = new XMLHttpRequest() || new ActiveXObject("Microsoft.XMLHTTP");
reader.open('GET', filename, async);
reader.onloadend = function () {return reader.responseText;}; // Lost to /dev/null while the program executes without this data. Unacceptable!
reader.overrideMimeType('text/plain');
try
{reader.send();}
catch (e)
{return null;}
if (!async) {return reader.responseText;}
//TODO: Find some way to delay execution without generating an infinite loop (good luck - javascript is not multithreaded so we cannot use a while loop for this)
//TODO: Extract reader.responseText from within reader.onloadend() (probably by invoking a global temp object to shift the data out of onloadend()).
};
Promises can help you make your asynchronous code more manageable, but it is not required.
In your case, you could simply use event listeners.
var xhr = new XMLHttpRequest() || new ActiveXObject("Microsoft.XMLHTTP");
xhr.responseType = 'text';
xhr.open('GET', 'https://jsonplaceholder.typicode.com/posts/1');
xhr.onload = function(){
var res = JSON.parse(this.responseText);
// do something with the response
document.write('<h1>' + res.title + '</h1>');
document.write('<p>' + res.body + '</p>');
}
xhr.send();
In other words, you need to refactor your code to remove the need to call for a function that returns something (or, simply, your function should call the rest of the code that requires this xhr through an event handler).
You can read more about the XHR requests and their possible results, events, etc. here.
in order to birng your function up to date, you should encapsulate it into a Promise. Here is an older post explaining:
How to promisify native XHR
With callback
function makeRequest (method, url, done) {
var xhr = new XMLHttpRequest();
xhr.open(method, url);
xhr.onload = function () {
done(null, xhr.response);
};
xhr.onerror = function () {
done(xhr.response);
};
xhr.send();
}
// And we'd call it as such:
makeRequest('GET', 'http://example.com', function (err, datums) {
if (err) { throw err; }
console.log(datums);
});

Javascript XmlHttpRequest callback

Trying to get my head around http requests and asynchronous javascript behaviour.
function httpGetAsync(url, callback){
var xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = function() {
if(xmlHttp.readyState == 4 && xmlHttp.status == 200){
console.log(xmlHttp.responseText); // prints the ip address
callback(xmlHttp.responseText);
}
}
xmlHttp.open("GET", url, true)
xmlHttp.send(null);
}
httpGetAsync("http://httpbin.org/ip", function(){
console.log(this); // doesn't print the ip address
})
The http request is just a simple one that returns a json formatted ip address of the get request. In the function definition the ip address can be printed to the console just fine however in the callback the console prints out the window object. this is probably the wrong thing to use, how do I access the data in xmlHttp.responseText in the callback?
callback(xmlHttp.responseText);
You are calling a function. You are passing it an argument.
function(){
console.log(this); // doesn't print the ip address
}
Your function is not expecting to receive any arguments. Change that, then use that argument.
function(my_argument){
console.log(my_argument);
}
See also How does the “this” keyword work?
You're calling callback(xmlHttp.responseText), so...
httpGetAsync("...", function(response) {
console.log(response);
});
Simple!
One small point: I would recommend putting the .open call before the onreadystatechange, because in some (older) browsers calling .open will reset event handlers.

Can I guarantee that onreadystatechange will eventually always get called with readyState == 4?

I'm using XMLHttpRequest on an embedded device that provides a non-standard extension to the API to allow manual cleanup of resources after the request has finished.
Can I assume that, for all cases (successful or otherwise, eg 404, DNS lookup failed, etc), a call to the send() method will eventually result in my onreadstatechange handler being called with readyState == 4?
Or, to put it another way, assuming that this implementation's XHR behaves similarly to that of the standard browsers in all other ways, will the following code always result in the destroy() method being called?
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
callback(xhr.responseText);
if (xhr.destroy) { // must call this to prevent memory leak
xhr.destroy();
}
}
};
xhr.open(method, url, true);
xhr.send(null);
No.
In some cases, for example when calling abort(), the state may terminate at UNSENT (3.6.5).
Even during "normal" operation, if an error occurs and an exception is thrown, then the state may terminate at something other than DONE.
Read the spec's section on states for more information.

How to add callback to AJAX variable assignment

I've got a variable responce which is assigned via an AJAX function send(). When I make the assignment...
responce = send();
response returns before send does giving me back an undefined how can I add a callback to prevent this from happening?
EDIT: a clarification on what I'm asking..
It's still returning undefined. I'm assigning the variable with the function send send returns onreadystatechange however when my code is executing.. response returns as undefined before send() can return. How do I stop the code under response from running on till response has been assigned sends value?
EDIT2:
The following code is my send function...
function send(uri)
{
var xhr = new XMLHttpRequest();
xhr.open("POST",uri,true);
xhr.onreadystatechange = function (send){
if(xhr.readyState == 4){
return xhr.responseText;
}
}
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
xhr.send(null);
}
You are using Ajax in a asynchronous manner, but you are treating it to be synchronous.
Asynchronous calls goes off to do its job while the rest of the code after the initial send call executes. You are getting undefined because the code is not returning anything.
If you look at the XMLHttpRequest object, the 3rd argument in the open method is the asynchronous flag.
open("method", "URL"[, asyncFlag[, "userName"[, "password"]]])
Setting it to true [default is left off] will make an asynchronous call. Setting it to false will make it synchronous.
The problem with using synchronous calls is it locks up the user's browser until the call is returned. That means animated gifs stuff, browser timers stop, and so on. If the server takes forever to respond, the user can not do anything.
The best solution is to avoid using synchronous calls. Use the call back to continue the code execution flow.
So based on your edit, I will edit my response with a basic solution
function send(uri, callback)
{
var xhr = new XMLHttpRequest();
xhr.open("POST",uri,true);
xhr.onreadystatechange = function (send){
if(xhr.readyState == 4){ //You really should check for status here because you can get 400, 500, etc
callback(xhr.responseText);
//return
}
}
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
xhr.send(null);
}
function myFunction(){
var myUrl = "foo.php";
//show a loading message or soemthing
var someDiv = document.getElementById("loadingMessage");
someDiv.style.display = "block";
//Second half of your function that handles what you returned.
function gotData( value ){
someDiv.style.display = "none";
alert(value);
}
send(myUrl, gotData);
}
If you really want to do synchronous and you do not mind locking up a user's browser
function send(uri, callback)
{
var xhr = new XMLHttpRequest();
xhr.open("POST",uri,false);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
xhr.send(null);
if(xhr.status==200){
return xhr.responseText;
}
else{
return null;
}
}
I presume you are talking about the XMLHTTPRequest.send() method, rather than a framework's wrapper.
send does not return anything. It will always return undefined.
To get the HTTP response, you need to monitor onreadystatechange, as countless StackOverflow answers and internet tutorials (e.g. this one) demonstrate.
you must assign the value to your response on readystatechange event of your request, something like this:
req.onreadystatechange = function(){
if (req.readyState===4) { // checks if data is already loaded.
callback(req.responseText,req.status); //call your callback function with response and status as parameters.
}
};
try this:
function send(uri, callback)
{
var xhr = new XMLHttpRequest();
xhr.open("POST",uri,true);
xhr.onreadystatechange = function (send){
if(xhr.readyState == 4 and callback){
callback();
}
}
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
xhr.send(null);
}
send("uri here", function(){
//whatever needs to be done after request
})

Categories

Resources