XMLHTTPRequest keeps looping through conditional statements? - javascript

I am trying to run a conditional statement based on the readystate of my xhr. The readystate switches to 1 after open() is called (line 2), but never changes after that. It skips over the conditionals, and nothing is sent.
I'd love to know why readystate is not changing?
var xhr = new XMLHttpRequest();
xhr.open('POST', submitUrl, true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
function1();
function2();
} else if (xhr.status === 400) {
function3();
function2();
} else if (xhr.status != 400 && xhr.status != 200) {
function5();
function6();
}
}
xhr.send(body);
})

According to your code, your observations, and the context you've provided in the comments, you've two issues:
sending form data
evaluate the response
Let's assume some basic form like this:
<form action="endpoint.php" method="post">
<input type="hidden" name="token" value="value">
<input type="submit" name="submit" value="submit">
</form>
To be able to send this form's data ourselves, we need to make sure we're intercepting the browser's default behaviour of submitting it right away as soon as the submit button is clicked (cf. also epascarello's comments):
// Intercept the onsubmit event
document.querySelector('form').onsubmit = function (evt) {
// Make sure to prevent the form from being submitted by
// the browser, which is the default behaviour.
evt.preventDefault();
// Get the form's data
let form = new FormData(evt.target);
// We're going to explicitly submitting our data
// as JSON, so make sure it actually is JSON.
let data = JSON.stringify(Object.fromEntries(form)); // https://stackoverflow.com/a/55874235/3323348
sendRequest(data); // Our submit function, which we'll define next (see below)
};
Now, we'd be able to actually send the data, and to properly handle messages and status codes send back by the server. But first, let's have a quick look at your if clauses, because they might not work the way you expect them to. Especially because state and status aren't mutually exclusive - a readyState of 4 doesn't mean the server hasn't answered with an HTTP status code denoting an error (like a 404):
if (xhr.readyState === 4) {
console.log(xhr.status); // Could be any HTTP status code
} else if (xhr.status === 400) {
console.log(xhr.readyState); // Could be any readyState besides 4
} else if (xhr.status != 400 && xhr.status != 200) {
console.log(xhr.readyState); // Could be any readyState besides 4...
console.log(xhr.status); // ...and any HTTP status code besides a Bad Request (400) and an OK (200)
}
So let's tackle that part a bit different, while the rest of your code stays the same (though wrapped in a function):
function sendRequest(data) {
const xhr = new XMLHttpRequest();
xhr.open('POST', '/endpoint.php'); // All requests are asynchronous by default,
// so we can drop the third parameter.
xhr.setRequestHeader('Content-Type', 'application/json');
// Since we've just created a client and initialized
// a request, we'll receive notifications for states
// 2-4 only (instead of 0-4).
xhr.onreadystatechange = function () {
console.log(xhr.readyState); // Let's watch the readyState changing
// We're interested in the final result of our request only (state 4),
// so let's jump all other states.
if (xhr.readyState !== 4) {
return;
}
const status = xhr.status; // HTTP status code
const type = status.toString().charAt(0); // Get class of HTTP status code (4xx, 5xx, ...)
if ([4,5].includes(type)) {
console.log('An error occured', status, xhr.responseText);
return;
}
if (status == 200) {
console.log('OK', xhr.responseText);
return;
}
// Server answered with a status code of 1xx, 3xx, or > 200.
console.log('Unexpected response', status, xhr.responseText);
}
xhr.send(data);
}
Now, you should be able to successfully send form data (and send it as JSON) and evaluate the HTTP response status codes. Instead of using XMLHttpRequest, though, you might want to consider fetch() instead.
Misc:
https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/readyState
https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#events
https://developer.mozilla.org/en-US/docs/Web/API/FormData/Using_FormData_Objects
How to convert FormData (HTML5 object) to JSON
Send POST data using XMLHttpRequest

Related

How can we measure an XMLHttpRequest request execution time? The time server starts processig to the time response is ready

I am trying to measure the time an asynchronous http request is processed by an http server developed in nodejs. By that I mean the time from which the server informs the client starting to process the request, to the time the server finishes processing and returns the response back to the client.
So, accrding to XMLHttpRequest documentation for readyState I am under the impression that the time difference between when readyState == 3 to readyState == 4 gets me what I want. In practice I get almost 0 to be the time difference between the two. However my expectation, according to the code snippets below is to get something around to 2000+ ms.
Why is that and how can I roughly measure the processing time in practice?
Client side:
<script>
let begin;
const xhttp = new XMLHttpRequest();
xhttp.open("GET", "https://mydomain. com/API", true);
xhttp.send();
xhttp.onreadystatechange = function () {
if (this.readyState == 3) {
begin = Date.now();
}
if (this.readyState == 4 && this.status == 200) {
console.log("Execution time (ms):" + (Date.now() - begin));
}
};
</script>
server side ( as seen, processing a request takes around 2000ms)
let http = require('http');
let url = require('url');
let server = http.createServer(function (req, res) {
reqReceived++;
let q = url.parse(req.url, true);
res.writeHead(200, {
"Content-Type": "text/html",
"Access-Control-Allow-Origin": "*"
});
setTimeout(() => {
res.end(`response is ready`);
}, 2000);
});
server.listen();
You might argue that I should have put writing the head inside the timeout callback, however it would not still make any time difference for the client to receive readyState ==3 and == 4.
The right readyState to start your timer is 1. Node.JS will send headers only when you call res.end(response is ready);
I tried your code and scan it using Wireshark:
The row #4 corresponding to the request sent by your client. The #6 is the header is sent by your server with the body.
According to MDN:
state 2 => XHR received headers from server
state 3 => Response is being loaded (if chunk)
state 4 => Response parsed.
By testing 2, 3 or 4, you'll only test the time that XHR will process the response. But "response is ready" is very short and it explain why you got 0ms everytime.
You can use the following code
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if(this.readyState == 1) console.time("count");
if (this.readyState == 4 && this.status == 200) {
console.timeEnd("count");
}
};
xhttp.open("GET", "https://mydomain. com/API", true);
xhttp.send();
}
With this, you can check the time after the request has been opened. If you want to count the total time you can move the console.time before the open function call, and you can see on the network tab (chrome browser) that the timing is near the actual timing the browser counts.
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
console.timeEnd("count");
}
};
console.time("count");
xhttp.open("GET", "https://mydomain. com/API", true);
xhttp.send();
}
Note: if you get near 0ms for your request make sure you have disabled the cache of your request from the network tab
From MDN, readyState = 3 means Response's body is being received. Which I interpret as the request has already processed and the client is now downloading the response.
To achieve what you want I would suggest measure the time difference from when you send the request, just above xhttp.send(), and when the readyState is 3. If it was me, I would extend it to when readyState is 4. This gives more realistic times which an end user might experience and includes the time it takes for the request to go over the network.
As a side note may be try measuring the difference between readyState=2 and readyState=4. Per MDN docs readyState is 2 when the request has been sent and the client has received the headers back, which means the connection has been successful. So, it should give accurate representation of time spent during processing on server side and will eliminate any network delays.
I would love to know your results.
I would do something like the following:
function timeFetch() {
let begin
const xhttp = new XMLHttpRequest()
xhttp.open("GET", "https://myDomain. com/API", true)
xhttp.send()
xhttp.onreadystatechange = function () {
if (this.readyState == 1) {
begin = new Date(Date.now())
}
if (this.readyState == 4 && this.status == 200) {
let end = new Date(Date.now())
console.log(
"Execution time (ms):" + (begin.getMilliseconds() - end.getMilliseconds()))
}
}
}

data cant be fetched from local file in ajax [duplicate]

This question already has answers here:
"Cross origin requests are only supported for HTTP." error when loading a local file
(30 answers)
Closed 2 years ago.
[enter image description here][1]i was learning ajax . but when i tried fetching data from local file it showed me error due to cors.i tried installing allow-access control origin but it didnt work .please help me
this is my java script code
function loadData() {
// Create an XHR Object
const xhr = new XMLHttpRequest();
// OPEN
xhr.open('GET', '/data.txt', true);
// console.log('READYSTATE', xhr.readyState);
// Optional - Used for spinners/loaders
xhr.onprogress = function(){
console.log('READYSTATE', xhr.readyState);
}
xhr.onload = function(){
console.log('READYSTATE', xhr.readyState);
if(this.status === 200) {
// console.log(this.responseText);
document.getElementById('output').innerHTML = `<h1>${this.responseText}</h1>`;
}
}
// xhr.onreadystatechange = function() {
// console.log('READYSTATE', xhr.readyState);
// if(this.status === 200 && this.readyState === 4){
// console.log(this.responseText);
// }
// }
xhr.onerror = function() {
console.log('Request error...');
}
xhr.send();
// readyState Values
// 0: request not initialized
// 1: server connection established
// 2: request received
// 3: processing request
// 4: request finished and response is ready
// HTTP Statuses
// 200: "OK"
// 403: "Forbidden"
// 404: "Not Found"
}```
[this the image of output which is showing error .][2]
[1]: https://i.stack.imgur.com/4YOqJ.png
[2]: https://i.stack.imgur.com/Bh1km.png
If you want to persist data in your client, use localStorage.
If you want to download data from a data source, use a web service.You can easily create a simple web server using Node and Express, for example (plenty of resources online), but this is beyond the scope of this answer.

Retrieve response text of a request that has already been made

In my question from yesterday, I asked how to retrieve the full HTML content as text. Seems like XHR is the way. Now, however, I have a different problem.
I use this code to retrieve the content of the document as a string:
var req = new XMLHttpRequest();
req.open("GET", document.location.href);
req.onreadystatechange = function () {
if (req.readyState === 4 && (req.status === 200 || req.status == 0)) {
console.log(req.responseText);
}
};
req.send(null);
However, there is a slight delay that I'd like to avoid. I'm testing on localhost and Chrome DevTools says there's several milliseconds of "Stalled" time. In the "Size" column, it says "(from disc cache)", so I know I'm requesting something that the client already has.
Questions
Since the request is about something that already exists, can I somehow make the response instantaneous?
Can I access the original request (the one that is fired after typing the website URL) and access its response text?
My goal is to get the document as a string as soon as possible and without waiting for the DOM to load.
You could implement a cache:
function retrieve(url,callback){
if(localStorage.getItem(url)){
return callback(localStorage.getItem(url));
}
var req = new XMLHttpRequest();
req.open("GET", document.location.href);
req.onreadystatechange = function () {
if (req.readyState === 4 && (req.status === 200 || req.status == 0)) {
localStorage.setItem(url,req.responseText);
callback(req.responseText);
}
};
req.send(null);
}
retrieve("http://test.com",console.log);//takes its time
setTimeout(retrieve,1000,"http://test.com",console.log);//should be very fast...
Also if you want to get the current response, why dont you simply access the document bject?

Listening to Ajax calls without typing additional code in the AJAX call iteslf

I was planning for a long time now to create a JS function that listens for failed AJAX calls and does something(like error reporting to a separate server).
I could call the function on the Ajax-failed portion of the AJAX code itself, but I want my colleagues to write AJAX calls without any need to remember that they need to type anything in the Ajax-failed portion.
e.g:
We code stuff like this:
All the calls we want to handle errors for, go to pre-specified
ajax_controller2.php. So the file we target with our Ajax calls is
ALWAYS called ajax_controller2.php. There are other AJAX calls but
to different php files and we don't want to handle errors for them.
Is it possible to listen for failed AJAX calls only to a php file
called ajax_controller2.php without typing anything in the Ajax-failed
portion of the code?
The whole idea is that my colleagues just include a .js file in their HTML and the rest is done for them.
This is the AJAX calls we use:
var ajax = new XMLHttpRequest();
var params = //some parameters here;
ajax.open("POST", "ajax_controller2.php?m=somecase", true);
ajax.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
ajax.send(params);
ajax.onreadystatechange = function () {
var response = "";
if (ajax.readyState == 1) {
response += "Status 1: Server connection established ! <br/>";
} else if (ajax.readyState == 2) {
response += "Status 2: Request recieved ! <br/>";
} else if (ajax.readyState == 3) {
response += "Status 3: Processing Request ! <br/>";
} else if (ajax.readyState == 4) {
if (ajax.status == 200) {
var text = ajax.responseText;
} else {
console.log("Ajax failed"); //In this portion of the code I could just type down the name of the function that when triggered, sends an error report.
}
}
}
//If an error occur during the ajax call.
if (ajax.readyState == 4 && ajax.status == 404) {
console.log("Error during AJAX call");
}
}
}
You can also alter the prototype of XMLHttpRequest to also insert your event handler at the onreadystatechange.
In the code below I did that at the open method and using addEventListener so it won't mess or get lost with other event listeners even the ones added with .onreadystatechange = function...
function errorLoggingFunc(){ console.log('myFunc'); };
XMLHttpRequest.prototype._open = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open= function(){
if(!this._hasErrorLog)
{
this._hasErrorLog = true;
this.addEventListener('readystatechange', errorLoggingFunc);
}
this._open.apply(this, arguments);
};
This way you colleagues will keep using the XMLHttpRequest but your logging will always be there anyway.

using XMLHttpRequest to get a web page but only get one part of the html

I use the code below to get a web page(html)
var htmlString=null;
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://www.yahoo.com");//can change to any web address
xhr.onreadystatechange = function() {
htmlString=htmlString+xhr.responseText;
if(xhr.statusText=="200 OK\r" ){
log (global.htmlString.length);
}
}
but it always get one part of the page, rather than whole html code
Is there any parameter to set the length of the return html code?
Your comment welcome
There will be multiple readystatechange events. The request will only be completely done when xhr.readyState === XMLHttpRequest.DONE === 4.
Also, htmlString = null; ...; htmlString=htmlString+xhr.responseText; is bogus in many ways. First time around it will do htmlString = null + "text" === "nulltext. Afterwards it will add .responseText (as retrieved so far) again and again, while going through the states.
Also, on a related note, you should check xhr.status == 200, not xhr.statusText == randomString. Web servers aren't necessarily sending "OK" in case of 200.
Read the XMLHttpRequest documentation.
Something like this should work better:
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://www.yahoo.com");
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 /* DONE */) {
console.log(xhr.responseText.length);
// Do something with it...
}
}
xhr.send();

Categories