Pass the value of an xhr onload function globally - javascript

In an app I'm creating I have the below XMLHttpRequest and I'm trying to pass the results of data inside the xhr.onload() into an array that's created within the same parent function.
var url = 'http://api.soundcloud.com/resolve.json?'+resource+'&'+CLIENT_ID;
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.onload = function(){
var data = JSON.parse(xhr.responseText);
console.log(data.permalink_url);
};
xhr.send();
Below this I have the building blocks of an array and I'm trying to pass the results of data into the track string.
var metadata = {
id: val,
title: title,
url: posturl,
track: data.permalink_url
};
Everything I've tried thus far either returns undefined or function and now I'm totally out of ideas...

Ajax executes asynchronously (generally). This is vital to how Ajax works. What this means is that you can't count on the when the onload method will fire, or even if it will fire. What this means is that all code that depends on the xhr.responseText (result of the HTTP request) has to be done within the callback itself. For example:
xhr.onload = function () {
// This will execute second
doStuffWithData(JSON.parse(xhr.responseText));
}
// This will execute first
xhr.send();
var anything = anythingElse;

Like I said in the comment to my previous answer, you can change the one line to xhr.open('GET', url, false). It will make the request synchronous and will prevent everything else from running until the request completes. It will allow you to access xhr.responseText without waiting for an onload function.
CLIENT_ID = 'client_id=xxx';
var src = track,
match = src.match(/url=([^&]*)/),
resource = match[0],
stream = decodeURIComponent(match[1]) + '/stream/?' + '&' + CLIENT_ID;
var url = 'http://api.soundcloud.com/resolve.json?' + resource + '&' + CLIENT_ID;
var xhr = new XMLHttpRequest();
xhr.open('GET', url, false);
xhr.send();
parsedResults.push({
title: title,
track: JSON.parse(xhr.responseText)
});
You can see it in action here. It appears to be broken in Chrome for some reason. Works in Firefox though. I assume it's something to do with the CORS + 302 redirect + synchronous request.

I've gone for Explosion Pills solution. The only problem I encounter is that the links that get crawled don't always come back in the same order each time I run the node app. Realistically they should return in the order that they're crawled in (which would be newest posts first, right?) However this isn't always the case?

Related

Understanding of ajax complete call

I'm struggling with stabilizing Selenium automation for jQuery/AJAX application hence referred to
https://www.swtestacademy.com/selenium-wait-javascript-angular-ajax/
and it has ajaxComplete() method which has following code -
var callback = arguments[arguments.length - 1];
var xhr = new XMLHttpRequest();
xhr.open('GET', '/Ajax_call', true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
callback(xhr.responseText);
}
};
xhr.send();
);
I haven't work on JavaScript before and this code which I'm not able to understand completely. I've following questions with this, if someone can help to understand it -
What is Ajax_call in this? Is it generic call to check ajax completion? Or do I need to have my own endpoint there? If yes, does single end point enough or do I need to identify all calls and add them in the method?
Please check the documentation for XMLHttpRequest.open. If you do, you will the second argument listed is
url
A DOMString representing the URL to send the request to.
This means that it is simply the URL you want to request. I can be anything you want. The / prefix means that you are request relative to the root of the website (so if you are requesting from https://example.com/somedir/somepage the request will be made to https://example.com/Ajax_call.

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);
});

execute javascript before XML Request

I'm not very used to xml requests, but is there a way to execute code before a XMLHttpRequest. What I'm trying to do is, Send a request and open a new url. But since this takes a few seconds and I got a few requests at once, I want a loading screen to appear while sending requests. Here is my code so far:
function sendRequests(){
url = "placeholder";
url2 = "placeholder2";
displayObject("loadingScreen");
var xmlHttpCart = new XMLHttpRequest();
xmlHttpCart.open( "GET", url, true );
xmlHttpCart.send( null );
window.open(url2,"_self");
}
function displayObject(i) {
var id = "" + i;
document.getElementById(i).style.display = "inherit";
}
Unfortunately the displayObject() function is triggered after the request is finished.
thanks
The browser will finish running the JavaScript until it finishes whatever it is doing. This blocks repainting the display.
xmlHttpCart.open( "GET", url, false ); // false for synchronous request
This is one example of why using false there is deprecated and you should not do that.

Append something always to an XHR Request

I want to attach a JS Variable to every XHR request that will be issued from my single page web application.
What would be the best approach to achieve this? I don't want to establish special methods for sending with this attributes, as there may be third parties integrating with my application.
I thought about overriding the send method on the XHR Object but thats not considered good style either.
I can't use cookies due to requests being cross-domain.
Any better idea or approach to this?
Thank you!
-Alessandro
if you really want to extend the existing functionalities without adding any library-like function, you could solve this using monkey patching:
(function() {
var originalOpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function(method, url, async, user, password) {
var getParameter = 'foo=bar';
if(url.indexOf('?') === -1) {
url = url + '?' + getParameter;
} else {
url = url + '&' + getParameter;
}
console.log('you just got monkey-patched! url is now:', url);
originalOpen.call(this, method, url, async, user, password);
}
})();
var request = new XMLHttpRequest();
request.open('get', 'http://google.com');
see also this jsfiddle for a working example.
you can use the same technique when injecting stuff into the body of a request if you patch the send() function.
if you do, ensure you take care for the type of the data to be transmitted (see MDN). it doesn't make sense to append a parameter to a binary body ;)

Javascript: xmlhttprequest randomly stuck at Ready State 1

I've been working on a Windows gadget (meaning the "browser" is Internet Explorer) that queries specified subnet addresses for information. Now, it sometimes does this at a relatively quick pace (roughly every 5 seconds) and it works well enough. However, sometimes it will get stuck at ready state 1 and will just stay there forever. Whenever the gadget tries to redo the function for getting the xmlhttprequest and getting information from it it will stay at state 1. This is easily replicable when opening multiple instances of the gadget and then closing all but one of them. At that point, the one that's still open will almost always get stuck in this state. I feel like it might have something to do with them all accessing the same website, or it may just have to do with xmlhttprequests being stopped mid-transmission and that preventing another from working. Below is the relevant code.
//Reference to this for the inner function
var me = this;
var request = new XMLHttpRequest();
request.onreadystatechange = onReadyStateChange;
var url = this.url;
//Make the URL random to prevent being cached
url += ("&a=" + ((new Date()).getTime()));
Trace(DEBUG_COMM, "Sase.updateStatus url: " + url);
request.open("GET", url, true);
request.send(); // fire off the request, calls httpRequestReadyStateChange as things continue
Trace(DEBUG_COMM, "Request sent" + request.readyState);
function onReadyStateChange() {Trace(DEBUG_COMM, "Sase.httpRequestReadyStateChange: state=" + request.readyState);
if (4 == request.readyState) {
Trace(DEBUG_COMM, "Sase.httpRequestReadyStateChange: status=" + request.status);
if (request.status == 200) {
Trace(DEBUG_COMM, "retrieved html: " + request.responseText);
var results = request.responseText;
var resultsString = request.responseText.toString();
Trace(DEBUG_COMM, "results String: " + resultsString);
me.ParseStatusData(resultsString);
}
else {
//me.commError(request.status);
}
request = null;
}
}
Well it looks like I figured it out. I had a feeling it was an unresolved request, since it only happens when instances of it are closed (meaning that if one of them is closed while in communication with the server it stays in communication forever and no one else can access the server) and it appears that is the case. I made some modifications to the code in multiple areas and basically what it comes down to is when the gadget closes it makes sure to abort all of the requests. The requests are now instance variables (to allow for the aborting of them), but are still made new everytime they are needed.
For those who stumble across this and need a concrete code example, here you go.
I had the same problem and the solution was to re-use the XMLHttpRequest object, to ensure that any previous request was cancelled before initiating a new one. This won't work if you want to have multiple AJAX requests flying around but in my case triggering a new request meant that the last one was no longer needed.
All of the requests on my page went through the same XMLHttpRequest wrapper method which looked like this;
//Declare the XMLHttpRequest object to be re-used
var global_xHttpRequest = null;
function xmlHttpHandler(type, params, complete, postData) {
//Prevents an issue where previous requests get stuck and new ones then fail
if (global_xHttpRequest == null) {
global_xHttpRequest = new XMLHttpRequest();
} else {
global_xHttpRequest.abort();
}
//Parse out current URL
var baseURL = window.location.host;
var svc = "https://" + baseURL + "/processAction?";
var url = svc + params;
global_xHttpRequest.open(type, url, true);
//Add the callback
global_xHttpRequest.onreadystatechange = complete;
global_xHttpRequest.send(postData);
}
Which can be used like this:
xmlHttpHandler("GET", params, completeFnc);

Categories

Resources