i have xhr with for loop and it work very rare
for(var i = 0; i < this.files.length; i++) {
var xhr = new XMLHttpRequest();
xhr.upload.onprogress = function(e) {
};
xhr.onreadystatechange = function(e) {
if(this.readyState === 4) {
console.log(xhr.responseText);
}
};
var formdata = new FormData();
formdata.append("files", this.files[i]);
console.log(this.files[i]);
xhr.open('POST', 'slike.php');
xhr.send(formdata);
}
I'm calling this slike.php. And it work very well, but on responseText, it's not good, sometimes get only last file from loop, sometimes get two file(with same text). I don't know how ti fix it, i was looking everywhere, and can't found the answer.
XHR are asynchronous by default, so unless you specify otherwise (async = false in XHR open() method), your loop may have finished before the first XHR may have been initialized.
But the i in your code (this.files[i]) in the loop refers to the same i of your loop, so i may be assigned this.files.length-1 when the first XHR begins. Taht is why you always get only the last file.
That is why you have to create what is called a closure to make sure the index you are using is the one you really want to use.
Try this :
for (var i = 0; i < this.files.length; i++) {
(function(index, files) { // In this closure : parameters of a function in JS
// are available only in the function,
// and cannot be changed from outside of it
var xhr = new XMLHttpRequest(); // variables declared in a function in JS
// are available only inside the function
// and cannot be changed from outside of it
xhr.upload.onprogress = function (e) {
};
xhr.onreadystatechange = function (e) {
if (this.readyState === 4) {
console.log(xhr.responseText);
}
};
var formdata = new FormData();
formdata.append("files", files[index]); // `index` has nothing to do with `i`, now:
// if `i` changes outside of the function,
//`index` will not
console.log(files[index]); // Don't keep `console.log()` in production code ;-)
xhr.open('POST', 'slike.php');
xhr.send(formdata);
})(i, this.files)
}
Or if would want to really get the files sequentially :
var i = 0,
fileNb = this.files.length;
function getNextFile(file) {
var xhr = new XMLHttpRequest();
xhr.upload.onprogress = function (e) {
};
xhr.onreadystatechange = function (e) {
if (this.readyState === 4) {
console.log(xhr.responseText);
if (++i < fileNb) getNextFile(this.files[i]);
}
};
var formdata = new FormData();
formdata.append("files", file);
console.log(file); // Don't keep `console.log()` in production code ;-)
xhr.open('POST', 'slike.php');
xhr.send(formdata);
}
getNextFile(i);
console.log(xhr.responseText);
You are accessing the current value of xhr (which will usually be the last one created) there instead of the object to which the event handler is attached.
Use this instead of xhr as you did in the previous line.
Related
I would like know how to handle multiple xhr requests with vanilla JS. I want to open multiple html templates and load the page when all of them are ready. when I use few xhr.open() requests it will only return 1 template:
var xhr = new XMLHttpRequest();
xhr.onload = function() {
if(xhr.status === 200){
storage.append(xhr.responseText);
}
}
function getAsync(url) {
xhr.open('get', url);
xhr.send();
}
getAsync('localhost:3000/template1.html');
getAsync('localhost:3000/template2.html');
I understand that .open() only works 1 at a time.
So is it possible to load all teamplates asynchronously or should I just load one after another in a synchronous matter? Also, I wonder,if I should create multiple xhr = new XMLHttpRequest() objects so that I could run multiple .open()?
Thank you
You are using one variable to define the xhr request, and using that variable twice, thus overriding the variable the second time. You need to create a loop and use let xhr; instead of var xhr as let has a block scope, so each instance in the loop will be independently defined.
i.e. something like
// Create iterable list of URLS
let urls = ['localhost:3000/template1.html', 'localhost:3000/template2.html'];
// Loop through URLs and perform request
for(let i=0; i<urls.length; i++) {
let xhr = new XMLHttpRequest();
xhr.onload = function() {
if(xhr.status === 200){
storage.append(xhr.responseText);
}
}
xhr.open('get', urls[i]);
xhr.send();
}
Background
I am making a request every 5 seconds using XMLHttpRequest and I want to print my name when I receive the response.
To do this I am using onreadystatechange which allows me to define a callback function when I receive the answer.
Problem
To achieve this, I am using a class. When I first initiate the class I say my name immediately, and then I start a process using setTimeInterval to make a request every 5 seconds and see my name.
The problem is that I see my name the first time, but then I never see it again. The issue is that this seems to be in different context, and thus this.sayMyName() doesn't exist because it doesn't belong to the xhr object.
What I tried
To fix this I tried using scoping by following another StackOverflow question but unfortunately this remains undefined.
Code
class Cook {
constructor() {
// innitial call so we don't have to wait
//5 seconds until we see my name
this.getCookInfo();
setInterval(this.getCookInfo, 5000);
}
getCookInfo() {
var xhr = new XMLHttpRequest(),
method = "GET",
url = "https://best.cooks.in.the.world.org/";
xhr.open(method, url, true);
//Call a function when the state changes.
xhr.onreadystatechange = (self => {
return () => {
if (xhr.readyState == XMLHttpRequest.DONE && xhr.status == 200)
self.sayMyName();
};
})(this);
}
sayMyName() {
console.log("Heisenberg");
}
}
Questions:
Is there a way to fix this code without have to pass a context object to the setInterval function?
Note
Kudos++ for those of you who get my reference :P
bind the this.getCookInfo function to this
then you can rally simplify your code
class Cook {
constructor() {
// innitial call so we don't have to wait
//5 seconds until we see my name
this.getCookInfo();
setInterval(this.getCookInfo.bind(this), 5000);
}
getCookInfo() {
var xhr = new XMLHttpRequest(),
method = "GET",
url = "https://best.cooks.in.the.world.org/";
xhr.open(method, url, true);
//Call a function when the state changes.
// no need for self gymnastics any more
// using onload, no need to test reasyState either
xhr.onload = e => {
if (xhr.status == 200)
this.sayMyName();
};
// your code lacks one thing
xhr.send();
}
sayMyName() {
console.log("Heisenberg");
}
}
An alternative -
class Cook {
constructor() {
this.getCookInfo();
}
getCookInfo() {
var getit = () => {
var xhr = new XMLHttpRequest(),
method = "GET",
url = "https://best.cooks.in.the.world.org/";
xhr.open(method, url, true);
//Call a function when the state changes.
xhr.onload = e => {
if (xhr.readyState == XMLHttpRequest.DONE && xhr.status == 200)
this.sayMyName();
};
xhr.send();
};
getit();
setInterval(getit, 5000);
}
sayMyName() {
console.log("Heisenberg");
}
}
I'm only 99% sure this is right though :p
The file uploads work perfectly, I can't get the progess and load events to callback
I have the following in a JavaScript file as my WebWorker code:
UploadFileWorker.js
function uploadFile(url, m) {
var f = m.file;
var fd = new FormData();
fd.append('file', f, f.name);
var xhr = new XMLHttpRequest();
xhr.upload.addEventListener('progress', function (e) {
m.set('status', (e.loaded / e.total) * 100 );
postMessage(m); // <-- never makes the call, doesn't throw and error either
});
xhr.upload.addEventListener('load', function (e) {
m.set('status',100);
postMessage(m); // <-- never makes the call, doesn't throw and error either
});
xhr.open('POST', url, true);
xhr.send(fd);
}
function getUploadURL(m) {
var xhr = new XMLHttpRequest();
xhr.addEventListener('load', function () {
var url = this.responseText;
uploadFile(url,m);
});
xhr.open('GET', '/upload', false);
xhr.send();
}
var q = [];
onmessage = function(e) {
postMessage(e.data);
q.push(e.data);
while (q.length > 0) {
var m = q.pop();
getUploadURL(m);
}
};
The postMessage(e.data); in the onmessage function work perfectly.
The postMessage(m); in the xhr.upload.addEventListeners() callbacks never happen. They worked fine before I tried and move these functions into WebWorker code.
Is this a scope issue? If so, how do I prefix the calls to get them to work.?
For completeness here is how I am defining my instance of Worker and kicking it off.
onButtonClick: function(button, e, eOpts) {
var ugv = this.getUploadGridView();
var fs = this.getFileUploadStore();
var worker = new Worker('/js/UploadFileWorker.js');
worker.onmessage = function(e) {
console.log(e);
};
var selected = ugv.getSelectionModel().getSelection();
Ext.each(selected, function(m) {
var o = {};
o.name = m.get('name');
o.size = m.get('size');
o.status = m.get('status');
o.file = m.file;
worker.postMessage(o);
});
},
Again the actual uploading of the files works great, I can't figure out how to call postMessage(); from inside the xhr callbacks.
This is apparently a bug that has been fixed just recently.
Issue 71433002: Enable XHR upload progress events for Workers. (Closed)
Workaround until Chrome gets updated
xhr.addEventListener(
instead of
xhr.upload.addEventListener(
This has the drawback that progress only gets called for every 1MB, files smaller than 1MB never get a progress event fired.
I have a function which attempts to capture a return value from a calling function in the following manner:
var select = xhrRetrieve(projID);
Here is an example of the xhrRetrieve function:
function xhrRetrieve(projID) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if(xhr.readyState == 4) {
if(xhr.status == 200) {
var obj = $.parseJSON(xhr.responseText);
return obj.select.toString();
}
}
}
var url = "ajax.cgi";
var data = "action=retrieve-opp&proj-id=" + projID;
xhr.open("POST",url);
xhr.setRequestHeader("Content-Type","application/x-www-urlencoded");
xhr.send(data);
}
I am using jQuery in conjunction with straight JavaScript. Whenever I attempt to get the value of obj.select using:
var select = xhrRetrieve(projID);
Select always comes back undefined.
What am I doing wrong?
The function doesn't return anything
The moment you call your function, the (not currently present) return value is being assigned to select. At the same moment, your ajax request is being fired, which takes time to complete; the callback function will not be called until the ajax request has completed (and succeeded).
This should work:
function doStuffWithTheAjaxResponse(select) {
// do stuff
}
function xhrRetrieve(projID) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if(xhr.readyState == 4) {
if(xhr.status == 200) {
var obj = $.parseJSON(xhr.responseText);
doStuffWithTheAjaxResponse(obj.select.toString());
}
}
}
var url = "ajax.cgi";
var data = "action=retrieve-opp&proj-id=" + projID;
xhr.open("POST",url);
xhr.setRequestHeader("Content-Type","application/x-www-urlencoded");
xhr.send(data);
}
Since the request is asynchronous the function will return before your code in onreadestatechange fires. You can switch to synchronous and get the value before the function returns:
function xhrRetrieve(projID) {
var returnVal;
var xhr = new XMLHttpRequest();
var url = "ajax.cgi";
var data = "action=retrieve-opp&proj-id=" + projID;
//3rd param is false to switch to synchronous
xhr.open("POST",url, false);
xhr.setRequestHeader("Content-Type","application/x-www-urlencoded");
xhr.send(data);
if(xhr.readyState == 4) {
if(xhr.status == 200) {
var obj = $.parseJSON(xhr.responseText);
return obj.select.toString();
}
}
}
The function xhrRetrieve doesn't have a return value. What do you expect to happen?
You have two functions there. The inner function returns a value, but not the outer one. The inner function is an event handler so the return value doesn't go anywhere. Your XMLHttpRequest is asynchronous, so you won't get a return value right away. See this post for a more detailed explanation: parameter "true" in xmlHttpRequest .open() method
I'm new to Ajax and this has me stumped. It's relatively simple but trips up on the onclick handler with the first xhr.open(). Firebug says it's not a function, my guess is the XMLHttpRequest object isn't being created, but I'm not sure. Anyone have any ideas?
Thanks in advance.
function init(){
function getXMLHTTP(){
var xhr = false;
if (window.XMLHttpRequest) {
xhr = new XMLHttpRequest();
}
else if (window.ActiveXObject) {
xhr = new ActiveXObject("Microsoft.XMLHTTP");
}
return xhr;
}
function updatePage(theData){
$('results').innerHTML = theData; //TODO: pretty this up with mootools
}
var xhr = getXMLHTTP();
if (xhr) {
xhr.onreadystatechange = function(){
if (xhr.readyState == 4) {
if (xhr.status == 200) {
var theData = xhr.responseText;
updatePage(theData);
}
else{
alert("Error communicating to web service!");
}
}
}
$('submit_btn').onclick = function(xhr){
if ($('state').value != "") {
var theValue = $('state').value;
xhr.open("GET", "/inc/calc.php?state="+theValue, true); //Ajax 101: 3rd argument marks object as asynchronous
xhr.send(NULL);
}
else if ($('city').value != "") {
xhr.open("GET", "/inc/calc.php?city="+$('city').value, true);
xhr.send(NULL);
}
else if ($('county').value != "") {
xhr.open("GET", "/inc/calc.php?county="+$('county').value, true);
xhr.send(NULL);
}
else {
//the form is empty, or just down payment is filled. returns whole freakin' table.
xhr.open("GET", "/inc/calc.php", true);
xhr.send(NULL);
}
}
}
}
The problem with your code is the onclick function. You have put the xhr in the argument list to the function. Remember that when a function is called, the value of this variable is set by the caller. In this case it would be the event dispatcher, and it would probably set the xhr variable to an event object, which does not have an open function.
If you remove the xhr variable from the argument list of the onclick function, then it will look for the xhr variable in the parent scope, the global scope, and it will find it there, and it should work. I haven't tested it though.
I'm assuming you are using some kind of framework as well (judging by the frequent use of $, and the reference to mootools). This framework probably has an ajax function built in, as well as a cross browser event model. Try using it instead, you will run into a lot less problems.
When assigning the onclick handler you create a new function that takes a parameter called xhr:
$('submit_btn').onclick = function(xhr){
...
xhr.open("GET", "/inc/calc.php?state="+theValue, true);
...
}
The click on the button won't pass a XMLHttpRequest object the the handler, so xhr will not have an open() method. The global definition of xhr doesn't matter because it's shadowed by the local parameter definition.
Generally you should just generate a new local XMLHttpRequest object when you need it, not try to use a global one. For example use an onclick function that creates a new local XMLHttpRequest:
$('submit_btn').onclick = function(){
var xhr = getXMLHTTP();
xhr.open("GET", "/inc/calc.php?state="+theValue, true);
...
}