Detect all XMLHttpRequest calls are completed - javascript

I have Javascript code that uploads files to a server. Each upload is done using a XMLHttpRequest object.
xhr = new XMLHttpRequest();
//...
xhr.open('POST', 'https://<bucket>.s3.amazonaws.com/', true);
xhr.send(fd);
The upload in parallel works fine. The problem is that I need to detect when all of them have finished, because I have to do a final submit, but only if all the uploads are completed.
My first try was save all the xhr objects in an array but I didn't know what to do with that :-(
var arrayxhr = [];
//...
//A loop {
xhr = new XMLHttpRequest();
arrayxhr.push(xhr);
xhr.open('POST', 'https://<bucket>.s3.amazonaws.com/', true);
xhr.send(fd);
//}
//And now?
I found this jQuery function https://api.jquery.com/ajaxcomplete/, but the same, I don't really know how to use it.
Can you help me please?
TIA,

If you can use jQuery you can use jQuery AJAX Deferred interface/methods and $.when method. $.ajax/$.post/$.get and other jQuery AJAX methods always return jQuery Deferred object:
$.when($.get('someUrl'), $.get('anotherUrl')).then(function () {
//all request complete
});
In native javascript you can use native Promise or any promise library:
http://www.javascriptoo.com/rsvp-js
http://www.javascriptoo.com/Q (example https://gist.github.com/matthewp/3099268)
Also good article about Promises - http://www.html5rocks.com/en/tutorials/es6/promises/.
Native Promise with XMLHttpRequest example:
function doAjaxRequest(method, url, data){
var promise = new Promise();
var xhr = new XMLHttpRequest();
xhr.open(method, url, true);
// Register the event handler
xhr.onload = function(){
if(xhr.status === 200){
promise.resolve("Done");
} else{
promise.reject("Failed");
}
};
data = data || {};
xhr.send(data);
return promise;
}
Promise.all(doAjaxRequest('post', 'someUrl'), doAjaxRequest('post', 'anotherUrl')).then(function (values) {
//all request complete
});

Well, this is not exactly the answer to my question (detect that the asynchronous calls are completed), but this answer works for me. I copy here just in case it helps someone else:
2: On the client-side, create a stack of files to upload and upload
one at a time, calling the next one on the "Complete" callback of the
previous.
https://stackoverflow.com/a/15557631/1893936

Related

Not able to load data from xml in plain javascript

I am trying to load my data from xml. Here is my code
function myFunction(){
debugger;
var url = "js/MyXml.xml";
var httpRequest = new XMLHttpRequest();
httpRequest.open('GET', url, true);
httpRequest.send();
var xmlDoc=httpRequest.responseXML;
}
Here in httpRequest i am getting status code is and httpRequest.responseXML = null
Cab you please help me what I am doing wrong.
In your case, you have used XMLHttpRequest call, that was Asynchronous. Also, you tried to assign the return value to your variable before the actual answer arrived, whether it was reading a file or actual response from the server.
In any case, if you decide to use Async (which is preferred way), then please observe the code provided below.
Also, I encourage you to do more investigation on your own, in the links provided below.
var url = "js/MyXml.xml";
var xhttp = new XMLHttpRequest();
var xmlDoc = null;
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
xmlDoc = httpRequest.responseXML;
}
};
xhttp.open("GET", url, true);
xhttp.send();
In addition to, you could also a fetch, which should be available in your javascript environment as well. According to MDN, this is still experimental technology, but it will provide beneficial knowledge.
And lastly, for the task at hand you could use a library, that is designed to make this task easier then native XMLHttpRequest. One of them is a jQuery. It could be easier to use it.
You can find more information here. Also easy to follow SO question.
Change it to that:
httpRequest.open('GET', url, false);
With the true parameter you're setting the request to be asynchronous.
Which means that you reading the response before the request finished executing.
Setting the last parameter to false will set the request to be synchronous so it will block your web page until the request finishes.
A proper way of doing it is creating an event on when the asynchronous request finishes.
Here is an example:
function myFunction() {
debugger;
var url = "js/MyXml.xml";
var httpRequest = new XMLHttpRequest();
httpRequest.open('GET', url, true);
httpRequest.onload = function(oEvent) {
var xmlDoc = httpRequest.responseXML;
//Continue code here
}
httpRequest.send();
}

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

JSON without jQuery

Using a version of the example from youmightnotneedjquery.com, I'm trying to get JSON into my page that is stored in the same folder as the HTML and JS. My problem is I'm not familiar with the XMLHttpRequest library and the answer I keep finding is "use jQuery or some other library." I added a console.log(); to the function so I could see if I was reaching success or failure because I'm not getting the data back. The original example is here and my code is below. The cv.json exists, is formatted correctly, and the function is sending Success? to the console, but I can't get the JSON data into my cv variable.
In case it is relevant, I'm hosting the JSON, HTML, and JS files in a public dropbox folder which doesn't seem to be part of the problem.
var cv;
request = new XMLHttpRequest();
request.open('GET', 'cv.json', true);
request.onload = function() {
if (request.status >= 200 && request.status < 400){
// Success!
console.log("Success?");
console.log(request.resonseText);
cv = JSON.parse(request.responseText);
} else {
// We reached our target server, but it returned an error
console.log("Error?");
}
};
request.onerror = function() {
// There was a connection error of some sort
};
request.send();
Note: There are lots of similar questions on stackoverflow but I haven't been able to find an answer to the specific issue I'm encountering; perhaps for people familiar with JavaScript this answer is too obvious to mention explicitly or I'm phrasing my searches incorrectly.
UPDATE: In the web inspector I can see the json file in the sources, with a response 200 and all the data so the file is accessible and being read, I'm just not getting it into the variable correctly apparently. Code updated to reflect corrected use of request.send();.
request.send does not return anything, it creates a handler that resolves a value. at the top level of this add:
var cv;
and then in the success part of the onload function change your return to:
cv = JSON.parse(request.responseText);
The two issues that were preventing the JSON from being show on the page, but still available from the console were
Not loading the variable correctly (resolved thanks to this answer)
Loading the file asynchronously! (resolved thanks to this similar question and it's answer)
Thanks to this comment, I went out and started my journey of learning about callbacks. The JSON load is now a function made as a callback. It's not optimized I'm sure, but sufficient for my current needs/abilities
Here is the working code. The significant change is the false on line 3.
var cv;
var loadJSON = function() {
request = new XMLHttpRequest();
request.open('GET', 'cv.json', true);
request.onload = function() {
if (request.status >= 200 && request.status < 400){
cv = JSON.parse(request.responseText);
} else {
// We reached our target server, but it returned an error
}
};
request.onerror = function() {
// There was a connection error of some sort
};
request.send();
};

Problems understanding/using callback function

In this discussion and in chat I understood that a callback is the only way to go!
"
Get from the server a link with ajax, write the link in a variable, open an xml with this link, doing some stuff with the xml: is callback the only way?
"
I'm trying to understand what a callback is. I read some blog, but I still have problems.
What I have now in JS is
1) a function to open an xml.
2) function to request the link of the xml in first function
Can someone provide an example in PLAIN JAVASCRIPT of how to nest these two functions?
The server generate the link of the xml because I'm making a multi user web site and every user has it's own xml. So I need to ask the server what is the link of the xml and then open it. Is there an easy way to achieve this? I need plain javascript no jquery.
Thanks!
In general, a "callback" is a function which will be executed at a later time when an asynchronous process is completed.
So you might start by defining the function that should happen when the data is retrieved from the server (the "second" function, intuitively, but you should define it first because it's the business functionality you're looking to achieve and not just an implementation concern). Something as simple as:
var doSomethingWithTheData = function () {
// do, well, something with the data
};
This assumes that you have the data, which you don't yet. But the AJAX call will get that data. You can now use this function as your callback for the AJAX call. Taking the AJAX example from MDN, you might have this:
var httpRequest;
if (window.XMLHttpRequest) { // Mozilla, Safari, ...
httpRequest = new XMLHttpRequest();
} else if (window.ActiveXObject) { // IE 8 and older
httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
}
httpRequest.onreadystatechange = doSomethingWithTheData;
// perform the AJAX request
The httpRequest object will contain the response from the server after the AJAX call is executed and completed. (Remember that this happens asynchronously, so it won't contain the response on the immediate next line of code. It will at some later time which you don't control. Hence the need for the callback.)
I recommend walking through that full MDN article to get all the details, particularly on handling errors and such. But the data you're looking for (assuming nothing went wrong) would be in httpRequest.ResponseText. So, also assuming your variables are scoped to allow this (you can modify that as needed):
var doSomethingWithTheData = function () {
var data = httpRequest.ResponseText;
// do, well, something with the data
};
Excuse me, #David
var httpRequest;
if (window.XMLHttpRequest) { // Mozilla, Safari,
httpRequest = new XMLHttpRequest();
} else if (window.ActiveXObject) { // IE 8 and older
httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
}
var url = "http://myserver.mydomain/getMyUsersXMLUrl?user=pete";
httpRequest.open("GET", url, true); // next ajax to retrieve XML - File
httpRequest.onreadystatechange = function() {
if (httpRequest.readyState == 4) { // response received
var response = httpRequest.responseText; // this should contain you url
httpRequest.open("GET", response, true); // next ajax to retrieve XML - File
// and the same as for the first request
}
}
If You just need to download a xml you dont need a callback . Just take a look at jquery and ajax. Retrieving a callback from a server is not possible . JSONP does deal with callbacks that are called by the server (actually they arent ) Butter you wont need it. I think you are messaging the xhttprequestobject
A callback function is simply a piece of executable code passed as a parameter to another piece of code. For example:
function first (arr, predicate) {
// no predicate supplied, return first element
if (!predicate) return arr[0];
for (var i = 0; i < arr.length; i++) {
// return first element satisfying predicate
if (predicate(arr[i])) return arr[i];
}
// no element satisfying predicate, return null
return null;
}
// second parameter is an anonymous function
// will alert 4, as it's the first element which will return true
alert(first([1, 2, 3, 4, 5, 6], function(n) { return n > 3; }));
Callbacks are useful for asynchronous tasks, or for library functions which need extra customization at runtime.

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