I have a very simple XMLHttpRequest making a POST call. It works on Chrome and Firefox. But when I tried it in IE10, I got this error..
SCRIPT5022: InvalidStateError
Here's the code.
function _ajaxHelper(options) {
var xhr = new XMLHttpRequest();
var opts = _extendHelper({
withCredentials: false,
method: 'GET'
}, options);
xhr.withCredentials = opts.withCredentials;
xhr.open(opts.method, opts.url);
xhr.setRequestHeader('Accept', 'text/plain');
xhr.send(JSON.stringify(options.data));
return {
done: function done(cb) {
xhr.onreadystatechange = function onStateChange() {
if (this.readyState === 4) {
if (this.status >= 200 && this.status < 300) {
cb(this.responseText);
} else {
cb(false);
}
}
};
xhr.ontimeout = function onTimeout() {
cb(false);
};
xhr.onerror = function onError() {
cb(false);
};
}
};
}
From what I googled, the reason that I would get that error was I set responseText but I didn't set anything and I'm getting instead. So, not sure what I did wrong there.
For _extendHelper this is the code
function _extendHelper() {
var args = arguments;
var res = {};
if (args && args[0]) {
for (var j in args) {
for (var k in args[j]) {
res[k] = args[j][k];
}
}
}
return res;
}
Related
My function has to call two endpoints and concat them in one string at the same time. My code is simply a function that is getting two endpoints at the same time and print it in console.
But the same function has to concat them to one string.
I tried to create separated variables contains each call and then simply concat them, but the result hadn't been any different.
I read about it for couple of hours, and I see no, even the smallest tip anywhere.
EDIT: Please mind that each endpoint is an actual array.
function endpointsToOneString() {
const Http = new XMLHttpRequest();
const url = 'https://baconipsum.com/api/?type=all-meat¶s=3&start-with-lorem=1&format=json';
Http.open("GET", url);
Http.send();
Http.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
console.log(Http.responseText)
}
}
const HttpTwo = new XMLHttpRequest();
const urlTwo = 'https://baconipsum.com/api/?type=all-meat¶s=3&start-with-lorem=1&format=json';
HttpTwo.open("GET", urlTwo);
HttpTwo.send();
HttpTwo.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
console.log(Http.responseText)
}
}
}
endpointsToOneString();
In this case you should use Promise feature of javascript.
Here you can learn how to promisify your native XHR. Morever, Here you can find about promise chaining.
I have just added Promise in your code but it needs to be refactored.
Update: From comment, you want your response texts as a plain string. But we are actually getting a JSON array as response. So, we need to parse it using JSON.parse() function to make it an array object. Then we need to use .join() method to join all element of the array into a string. See the code below:
function endpointsToOneString() {
var requestOne = new Promise(function(resolve, reject){
const Http = new XMLHttpRequest();
const url = 'https://baconipsum.com/api/?type=all-meat¶s=3&start-with-lorem=1&format=json';
Http.open("GET", url);
Http.onload = function () {
if (this.status >= 200 && this.status < 300) {
resolve(Http.response);
} else {
reject({
status: this.status,
statusText: Http.statusText
});
}
};
Http.onerror = function () {
reject({
status: this.status,
statusText: Http.statusText
});
};
Http.send();
});
var requestTwo = new Promise(function(resolve, reject){
const HttpTwo = new XMLHttpRequest();
const urlTwo = 'https://baconipsum.com/api/?type=all-meat¶s=3&start-with-lorem=1&format=json';
HttpTwo.open("GET", urlTwo);
HttpTwo.onload = function () {
if (this.status >= 200 && this.status < 300) {
resolve(HttpTwo.response);
} else {
reject({
status: this.status,
statusText: HttpTwo.statusText
});
}
};
HttpTwo.onerror = function () {
reject({
status: this.status,
statusText: HttpTwo.statusText
});
};
HttpTwo.send();
});
Promise.all([
requestOne,
requestTwo
]).then(function(result){
var response = JSON.parse(result[0]).join();
response += JSON.parse(result[1]).join();
console.log(response);
});
}
endpointsToOneString();
I understand you want to concat the result of two parallel requests. In that case you can use a library like axios. From their docs
function getUserAccount() {
return axios.get('/user/12345');
}
function getUserPermissions() {
return axios.get('/user/12345/permissions');
}
axios.all([getUserAccount(), getUserPermissions()])
.then(axios.spread(function (acct, perms) {
// Both requests are now complete
}));
So for your example:
function getEndpoint1() {
return axios.get('https://baconipsum.com/api/?type=all-meat¶s=3&start-with-lorem=1&format=json');
}
function getEndpoint2() {
return axios.get('https://baconipsum.com/api/?type=all-meat¶s=3&start-with-lorem=1&format=json');
}
axios.all([getEndpoint1(), getEndpont2()])
.then(axios.spread(function (resp1, resp2) {
// Both requests are now complete
console.log(resp1 + resp2)
}));
try to have a look on the Promise.all method:
https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
As in this answer you should wrap your XHR in a Promise and then handle resolving of all function call. In this way you can access endpoint results in order.
Here's a working fiddle:
function makeRequest(method, url) {
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open(method, url);
xhr.onload = function() {
if (this.status >= 200 && this.status < 300) {
resolve(xhr.response);
} else {
reject({
status: this.status,
statusText: xhr.statusText
});
}
};
xhr.onerror = function() {
reject({
status: this.status,
statusText: xhr.statusText
});
};
xhr.send();
});
}
let url1 = 'https://baconipsum.com/api/?type=all-meat¶s=3&start-with-lorem=1&format=json';
let url2 = 'https://baconipsum.com/api/?type=all-meat¶s=3&start-with-lorem=1&format=json'
Promise.all([makeRequest('GET', url1), makeRequest('GET', url2)])
.then(values => {
debugger;
console.log(values);
});
https://jsfiddle.net/lbrutti/octys8k2/6/
Is it obligatory for you to use XMLHttpRequest? If not, u had better use fetch, because it returns Promise and with Promise it would be much simpler.
Rather than immediately printing them, save them to local variables, then print them at the end:
function endpointsToOneString() {
let response; // this line here declares the local variable
results = 0; // counts results, successful or not
const Http = new XMLHttpRequest();
const url = 'https://baconipsum.com/api/?type=all-meat¶s=3&start-with-lorem=1&format=json';
Http.open("GET", url);
Http.send();
Http.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
response = Http.responseText; //save one string
}
if (this.readyState == 4) {
results++;
}
}
const HttpTwo = new XMLHttpRequest();
const urlTwo = 'https://baconipsum.com/api/?type=all-meat¶s=3&start-with-lorem=1&format=json';
HttpTwo.open("GET", urlTwo);
HttpTwo.send();
HttpTwo.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
response += HttpTwo.responseText // save the other string
}
if (this.readyState == 4) {
results++;
}
}
while(results < 2) {} //loops until both requests finish, successful or not
console.log(response); //print total string
}
endpointsToOneString();
Also, HttpTwo's onreadystatechange function is calling for Http.responseText, rather than HttpTwo.responseText. Fix that as well for best results.
EDIT: Thanks for the tip, Jhon Pedroza!
EDIT: Noah B has pointed out that the above is dirty and inefficient. They are entirely correct. Better version based on their suggestion, credit to them:
function endpointsToOneString() {
let response1 = '', response2 = ''; // this line declares the local variables
const Http = new XMLHttpRequest();
const url = 'https://baconipsum.com/api/?type=all-meat¶s=3&start-with-lorem=1&format=json';
Http.open("GET", url);
Http.send();
Http.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
response1 = Http.responseText; //save one string
checkResults(response1, response2);
}
}
const HttpTwo = new XMLHttpRequest();
const urlTwo = 'https://baconipsum.com/api/?type=all-meat¶s=3&start-with-lorem=1&format=json';
HttpTwo.open("GET", urlTwo);
HttpTwo.send();
HttpTwo.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
response2 = HttpTwo.responseText; // save the other string
checkResults(response1, response2);
}
}
}
function checkResults(r1, r2) {
if (r1 != '' && r2 != '') {
console.log(r1 + r2);
}
}
endpointsToOneString();
function endpointsToOneString() {
var response;
const Http = new XMLHttpRequest();
const url = 'https://baconipsum.com/api/?type=all-meat¶s=3&start-with-lorem=1&format=json';
Http.open("GET", url);
Http.send();
Http.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
response = this.responseText;
HttpTwo.open("GET", urlTwo);
HttpTwo.send();
}
}
const HttpTwo = new XMLHttpRequest();
const urlTwo = 'https://baconipsum.com/api/?type=all-meat¶s=3&start-with-lorem=1&format=json';
HttpTwo.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
response += this.responseText;
console.log(response);
}
}
}
endpointsToOneString();
check this out. there's just minimal editing to your code.
I am doing a XMLHttpRequest which is in it's own function. I want to return the price1 var to be used by other functions. But it seems to be lost in the scope. Code below -
var dataControl = (function () {
var getPrice = function(sym){
var xhttp = new XMLHttpRequest();
var response, price1;
xhttp.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
response = JSON.parse(this.responseText);
price1 = response['Data'];
price1 = price1[0].open;
console.log(price1);
return price1;
}
};
xhttp.open("GET", "https://min-api.cryptocompare.com/data/histominute?fsym=XRP&tsym=USD&limit=60&aggregate=3&e=CCCAGG", true);
xhttp.send();
}
return {
getItem: function () {
getPrice();
}
}
})();
You can use the Callback Paradigm to get the data that you need
var dataControl = (function () {
var getPrice = function (cb) {
cb = (typeof cb === 'function' && cb) || function(){};
var xhttp = new XMLHttpRequest();
var response, price1;
xhttp.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
response = JSON.parse(this.responseText);
price1 = response['Data'];
price1 = price1[0].open;
console.log(price1);
// Assuming error first callback paradign
cb(null, price1);
}
};
xhttp.open("GET", "https://min-api.cryptocompare.com/data/histominute?fsym=XRP&tsym=USD&limit=60&aggregate=3&e=CCCAGG", true);
xhttp.send();
}
return {
getItem: function (cb) {
getPrice(cb);
}
}
})();
dataControl.getItem(function(err, data){
if(err){
// Handle error
return;
}
console.log(data); // Value of price1
});
I am using AJAX GET to get a local JSON file and it does that, but once i try to return it says undefined.
ScoreHandler = function () {
this.getScores = function() {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
var data = JSON.parse(this.responseText);
//This logs object
console.log(data);
return data;
}
};
xmlhttp.open("GET", "JSON/Scores.json", true);
xmlhttp.send();
};
};
HighScores = function (scoreHandler) {
var scoreHandler = scoreHandler;
var scores = this.scoreHandler.getScores();
//This logs undefined
console.log(scores);
}
Just implement a callback for response, something like this
ScoreHandler = function () {
this.getScores = function(callback) {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
var data = JSON.parse(this.responseText);
//This logs object
console.log(data);
if(typeof callback === 'function')
callback(data);
//return data;
}
};
xmlhttp.open("GET", "JSON/Scores.json", true);
xmlhttp.send();
};
};
HighScores = function (scoreHandler) {
var scoreHandler = scoreHandler; //why this line use it directly
var scores = this.scoreHandler.getScores(function(data){
console.log("response", data); //you can see the data here
});
//This logs undefined
console.log(scores);
}
I'm using csrfMagic as an automatic CSRF protection for my project. when I include the script in my project XMLHTTPRequest events don't work.
for example 'onprogress' event is not triggered.
my code:
var xhr = new XMLHttpRequest()
xhr.open("POST", "./");
xhr.onreadystatechange = function (e) {
//some code
}
xhr.upload.onprogress = function (e) {
//this event is not triggered
console.log('xhr.upload.onprogress was triggerd');
}
csrfMagic script:
/**
* #file
*
* Rewrites XMLHttpRequest to automatically send CSRF token with it. In theory
* plays nice with other JavaScript libraries, needs testing though.
*/
// Here are the basic overloaded method definitions
// The wrapper must be set BEFORE onreadystatechange is written to, since
// a bug in ActiveXObject prevents us from properly testing for it.
CsrfMagic = function(real) {
// try to make it ourselves, if you didn't pass it
if (!real) try { real = new XMLHttpRequest; } catch (e) {;}
if (!real) try { real = new ActiveXObject('Msxml2.XMLHTTP'); } catch (e) {;}
if (!real) try { real = new ActiveXObject('Microsoft.XMLHTTP'); } catch (e) {;}
if (!real) try { real = new ActiveXObject('Msxml2.XMLHTTP.4.0'); } catch (e) {;}
this.csrf = real;
// properties
var csrfMagic = this;
real.onreadystatechange = function() {
csrfMagic._updateProps();
return csrfMagic.onreadystatechange ? csrfMagic.onreadystatechange() : null;
};
csrfMagic._updateProps();
}
CsrfMagic.prototype = {
open: function(method, url, async, username, password) {
if (method == 'POST') this.csrf_isPost = true;
// deal with Opera bug, thanks jQuery
if (username) return this.csrf_open(method, url, async, username, password);
else return this.csrf_open(method, url, async);
},
csrf_open: function(method, url, async, username, password) {
if (username) return this.csrf.open(method, url, async, username, password);
else return this.csrf.open(method, url, async);
},
send: function(data) {
if (!this.csrf_isPost) return this.csrf_send(data);
prepend = csrfMagicName + '=' + csrfMagicToken + '&';
if (this.csrf_purportedLength === undefined) {
this.csrf_setRequestHeader("Content-length", this.csrf_purportedLength + prepend.length);
delete this.csrf_purportedLength;
}
delete this.csrf_isPost;
return this.csrf_send(prepend + data);
},
csrf_send: function(data) {
return this.csrf.send(data);
},
setRequestHeader: function(header, value) {
// We have to auto-set this at the end, since we don't know how long the
// nonce is when added to the data.
if (this.csrf_isPost && header == "Content-length") {
this.csrf_purportedLength = value;
return;
}
return this.csrf_setRequestHeader(header, value);
},
csrf_setRequestHeader: function(header, value) {
return this.csrf.setRequestHeader(header, value);
},
abort: function() {
return this.csrf.abort();
},
getAllResponseHeaders: function() {
return this.csrf.getAllResponseHeaders();
},
getResponseHeader: function(header) {
return this.csrf.getResponseHeader(header);
} // ,
}
// proprietary
CsrfMagic.prototype._updateProps = function() {
this.readyState = this.csrf.readyState;
if (this.readyState == 4) {
this.responseText = this.csrf.responseText;
this.responseXML = this.csrf.responseXML;
this.status = this.csrf.status;
this.statusText = this.csrf.statusText;
}
}
CsrfMagic.process = function(base) {
var prepend = csrfMagicName + '=' + csrfMagicToken;
if (base) return prepend + '&' + base;
return prepend;
}
// callback function for when everything on the page has loaded
CsrfMagic.end = function() {
// This rewrites forms AGAIN, so in case buffering didn't work this
// certainly will.
forms = document.getElementsByTagName('form');
for (var i = 0; i < forms.length; i++) {
form = forms[i];
if (form.method.toUpperCase() !== 'POST') continue;
if (form.elements[csrfMagicName]) continue;
var input = document.createElement('input');
input.setAttribute('name', csrfMagicName);
input.setAttribute('value', csrfMagicToken);
input.setAttribute('type', 'hidden');
form.appendChild(input);
}
}
// Sets things up for Mozilla/Opera/nice browsers
// We very specifically match against Internet Explorer, since they haven't
// implemented prototypes correctly yet.
if (window.XMLHttpRequest && window.XMLHttpRequest.prototype && '\v' != 'v') {
var x = XMLHttpRequest.prototype;
var c = CsrfMagic.prototype;
// Save the original functions
x.csrf_open = x.open;
x.csrf_send = x.send;
x.csrf_setRequestHeader = x.setRequestHeader;
// Notice that CsrfMagic is itself an instantiatable object, but only
// open, send and setRequestHeader are necessary as decorators.
x.open = c.open;
x.send = c.send;
x.setRequestHeader = c.setRequestHeader;
} else {
// The only way we can do this is by modifying a library you have been
// using. We support YUI, script.aculo.us, prototype, MooTools,
// jQuery, Ext and Dojo.
if (window.jQuery) {
// jQuery didn't implement a new XMLHttpRequest function, so we have
// to do this the hard way.
jQuery.csrf_ajax = jQuery.ajax;
jQuery.ajax = function( s ) {
if (s.type && s.type.toUpperCase() == 'POST') {
s = jQuery.extend(true, s, jQuery.extend(true, {}, jQuery.ajaxSettings, s));
if ( s.data && s.processData && typeof s.data != "string" ) {
s.data = jQuery.param(s.data);
}
s.data = CsrfMagic.process(s.data);
}
return jQuery.csrf_ajax( s );
}
}
if (window.Prototype) {
// This works for script.aculo.us too
Ajax.csrf_getTransport = Ajax.getTransport;
Ajax.getTransport = function() {
return new CsrfMagic(Ajax.csrf_getTransport());
}
}
if (window.MooTools) {
Browser.csrf_Request = Browser.Request;
Browser.Request = function () {
return new CsrfMagic(Browser.csrf_Request());
}
}
if (window.YAHOO) {
// old YUI API
YAHOO.util.Connect.csrf_createXhrObject = YAHOO.util.Connect.createXhrObject;
YAHOO.util.Connect.createXhrObject = function (transaction) {
obj = YAHOO.util.Connect.csrf_createXhrObject(transaction);
obj.conn = new CsrfMagic(obj.conn);
return obj;
}
}
if (window.Ext) {
// Ext can use other js libraries as loaders, so it has to come last
// Ext's implementation is pretty identical to Yahoo's, but we duplicate
// it for comprehensiveness's sake.
Ext.lib.Ajax.csrf_createXhrObject = Ext.lib.Ajax.createXhrObject;
Ext.lib.Ajax.createXhrObject = function (transaction) {
obj = Ext.lib.Ajax.csrf_createXhrObject(transaction);
obj.conn = new CsrfMagic(obj.conn);
return obj;
}
}
if (window.dojo) {
// NOTE: this doesn't work with latest dojo
dojo.csrf__xhrObj = dojo._xhrObj;
dojo._xhrObj = function () {
return new CsrfMagic(dojo.csrf__xhrObj());
}
}
}
Your code does not contain a call of send method of XMLHttpRequest. So csrf_magic can't add csrf token to HTTP-request.
Example of a correct upload function:
function upload(file) {
var xhr = new XMLHttpRequest();
xhr.upload.onprogress = function(event) {
log(event.loaded + ' / ' + event.total);
}
xhr.onload = xhr.onerror = function() {
if (this.status == 200) {
log("success");
} else {
log("error " + this.status);
}
};
xhr.open("POST", "upload", true);
xhr.send(file);
}
Links:
https://developer.mozilla.org/ru/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest
I'm using the following code to make multiple async. calls and I need to know how many of those calls are pending for validating purposes.
function llenarComboMetodos(cell) {
var xhr;
if (window.XMLHttpRequest) {
xhr = new XMLHttpRequest();
}
else if (window.ActiveXObject) {
xhr = new ActiveXObject("Msxml2.XMLHTTP");
}
else {
throw new Error("Las llamandas asincronas no son soportadas por este navegador.");
}
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status == 200 && xhr.status < 300) {
var combo = '<select name="metodos[]">';
var opciones=xhr.responseText;
combo+= opciones+"</select>";
cell.innerHTML = combo;
}
}
}
xhr.open('POST', 'includes/get_metodos.php');
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.send("completar=1");
}
Is there a way to know that?
Thank you (:
You can intercept the XMLHttpRequest.send and do your counting of active calls:
var activeXhr = (function(){
var count = 0;
XMLHttpRequest.prototype.nativeSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function(body) {
this.onreadystatechange = function(){
switch(this.readyState){
case 2: count++; break
case 4: count--; break
}
};
this.nativeSend(body);
};
return count;
})();
console.log(activeXhr);