Javascript: XMLHttpRequest Task Switch - javascript

document.getElementById('contactButton').value = "Sending";
xmlhttp.onreadystatechange=stateChanged;
xmlhttp.open("GET",url,false);
xmlhttp.send(null);
function stateChanged(){
if (xmlhttp.readyState==4) {
var response = xmlhttp.responseText;
if(response == "true"){
document.getElementById('contactButton').value = "Sent :)";
}
}
When running this javascript, contactButton never gets set to "Sending...". It hangs for one second, and then changes to "Sent :)".
I'm not real sure about the processing order of javascript, but it seems like it needs some sort of task switch to process the XMLHttpRequest().
This is obviously an abbreviated code, but I have several other javascript/css things I am trying to do before the xmlhttp. It seems like the xmlhttp just takes over when the request is sent.
Any ideas?

You're passing false to open, so it's running synchronously. That means it doesn't use the readyState and instead just delays until the request completes. If you want to do sync, it should be:
xmlhttp.open("GET",url,false);
xmlhttp.send(null);
if(xmlhttp.status == 200)
{
var response = xmlhttp.responseText;
document.getElementById('contactButton').value = "Sent :)";
}
You should change to async to avoid hanging your script, and you will probably find it easier to use a JavaScript library.

Related

Why a code continues when the function doesnt return value yet. (in xmlhttprequest, calling php file.) Is it related to Sync/Async functions?

Came here again with lame questions as I am in process of learning/coding.
I would like to change a property of a disable value on button: During function performing its job, button should be disabled, once function finishes and return the values, button should be enabled again.
In function which creates a buttons I am calling update() function which loading php file via XMLHttpRequest. Then running the php code and return values on page. I want to have button disabled during this time. But everytime I call the function the button will not change. Or if changed it was so fast that I didnt even saw it.
here is a code:
global_button = document.createElement("button");
// let btn1 = document.createElement("button");
global_button.innerHTML = "UPDATE";
global_button.id = "update";
global_button.disabled = false;
document.body.appendChild(global_button);
document.getElementsByClassName("two")[0].append(global_button);
global_button.addEventListener("click", function () {
console.log("After CLICKED");
global_button.disabled = true;
update();
global_button.disabled = false;
console.log("AFTER FUNCTION RETURN VALUES");
update function:
var xmlhttp;
function loadFile(file, func){
xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = func;
xmlhttp.open("GET", file, true);
xmlhttp.send();
}
function update(){
loadFile("update.php", function(){
if (xmlhttp.readyState == 4 && xmlhttp.status == 200){
document.getElementById("content").innerHTML = xmlhttp.responseText;
}
});
}
When I checked the console, it shows both console logs immediately: "After CLICKED" and "AFTER FUNCTION RETURN VALUES" messages. And couples seconds later, result of the function appear. But button wont change whatsoever.
I am suspecting the sync/async functions ? I read something for the .open method and vale true/false, but nothing changed if I switched from true to false. Also thinking if I should put it on the loop or something which will check the button clicked ? But I thought that listener would do the job.
Can anybody check and give me an advice ? or correct my thinking if it's wrong?
many thanks all of you. :)
The problem is indeed due to the asynchronous nature of the send method of XMLHttpRequest - and therefore of your update, which calls it.
When you call update(), which itself calls this:
loadFile("update.php", function(){
if (xmlhttp.readyState == 4 && xmlhttp.status == 200){
document.getElementById("content").innerHTML = xmlhttp.responseText;
}
});
all that happens is that you set up an XMLHttpRequest object and use its send method to send a request, telling it to call this function:
function(){
if (xmlhttp.readyState == 4 && xmlhttp.status == 200){
document.getElementById("content").innerHTML = xmlhttp.responseText;
}
});
as a "callback" when the readyState changes. (And in particular, when the request is complete and a response received.) But calling update does not wait for that state change to happen and block your code from running - hence the next lines of code, which set the disabled state of the button to false and log to the console - are executed straight away. So the button gets disabled but then instantly un-disabled, and therefore you never see it disabled. (In fact the browser will never even "paint" the screen with a disabled button, since it doesn't get a chance to do this while your code is running, so even if you could in theory do a freeze-frame here you would never see a disabled button.)
To fix it, you have to work with the asynchronous code you're using. Anything you want to happen after the state change has to take place in the callback function you pass it. So you can simply fix your problem by changing the update definition to this:
function update(){
loadFile("update.php", function(){
if (xmlhttp.readyState == 4 && xmlhttp.status == 200){
document.getElementById("content").innerHTML = xmlhttp.responseText;
global_button.disabled = false;
console.log("AFTER FUNCTION RETURN VALUES");
}
});
}
and delete those two lines of code from the place you've currently got them, after the update call.
Although note that this will only work if global_button is in scope inside update, which it might not be depending on how your code is structured (it probably shouldn't be to be honest). And even if it does, it's not good to hardcode your update to always undisable the button afterwards, with no guarantee the button will even be disabled first.
It's therefore better to define update to itself take a callback function:
function update(callback){
loadFile("update.php", function(){
if (xmlhttp.readyState == 4 && xmlhttp.status == 200){
document.getElementById("content").innerHTML = xmlhttp.responseText;
callback();
}
});
}
and then call it like this in your main code:
global_button.disabled = true;
update(function() {
global_button.disabled = false;
console.log("AFTER FUNCTION RETURN VALUES");
});
because this separates the concerns of update itself ("make this request and set the content inner HTML to the response"), from whatever you might want to do afterwards, which could be different each time.
Finally, I can't not mention that this callback-based asynchronous code is very old-fashioned now. XMLHTTPRequest itself is quite a cumbersome API. I highly recommend you look into its modern equivalent, fetch, which is based on Promises - which while not without their mental gotchas are a much more understandable way to write asynchronous code. In particular with async and await you can write code that looks much like what you originally had: putting await update(); would actually do what you are waiting, and have the rest of your code wait for update to complete. But you can't just make that change to your original code because that only works if update returns a Promise, which in turns would mean completely rewriting your loadFile to use a more modern, Promise-based approach.

Continuous DB calls with AJAX in straight JS

This is my first question here so I will start by saying: Hello to everyone!
I am trying to make a live data "presentation" in the form of a fancy table using straight JavaScript and Ajax, backend is PHP. In order to refresh the table I need to make requests at every, lets say 3 seconds, 4 is ok to. The database is RedisDB. I made the php script to fetch the data and its ok. I made a single JS file to request and handle/process the data and it's sort of ok.
The presentation looks great and the algorithm that is written in JS works excellent, it's about 600 lines, some simple if-else's and switches other a little more complex.
It now gets nasty. I can't get the freaking thing to do this continuously, I tried ways with setTimeout() and setInterval(), I made timers and infinite loops with sleep-like functions and so on, but it just can't survive that next request after the initial when the page loads.
The code for Ajax request is pretty straightforward for pure JS. I get my JSON string data parse it with jsonParse() and then do my processing of the data.
This all works, it's the next request that fails, no matter how it's initiated (user action or something else).
AJAX code:
window.onload = function(){
getEventdataFromDB();
}
function getEventdataFromDB(){
var xmlhttp;
if(window.XMLHttpRequest){
xmlhttp = new XMLHttpRequest();
}else{
xmlhttp = new ActiveXObject('Microsoft.XMLHTTP');
}
xmlhttp.onreadystatechange = function(){
if(xmlhttp.readyState == 4 && xmlhttp.status == 200){
var dbData = xmlhttp.responseText;
if(dbData != ''){
processEvents(dbData); //function for data processing
}
}
}
xmlhttp.open('GET','getEvents.php?dataRequest=true',true);
xmlhttp.send();
}
I know that it's a bit of a sin these days not to follow the flow and use jQuery and other frameworks but I'm just not big on the latest and greatest simplification of stuff that works just fine, to begin with.
What is the best way to do this, how should I proceed, what to use. I did some researched and things point to chaining callbacks and Promise objects.
There is obviously no way that I am the first one to do this so what is the way forward here.
The only way to ensure the previous request is over before starting the next one is so start the next call from the onreadystatechanged event of the previous one. Because ajax runs asynchronously, there's no guarantee whether any other code you run will execute before or after the ajax call finishes.
So you need to re-organise your script a bit:
var xmlhttp;
window.onload = function(){
setupAjaxObject();
getEventdataFromDB();
}
function setupAjaxObject()
{
if(window.XMLHttpRequest){
xmlhttp = new XMLHttpRequest();
}else{
xmlhttp = new ActiveXObject('Microsoft.XMLHTTP');
}
xmlhttp.onreadystatechange = function(){
if(xmlhttp.readyState == 4){
if(xmlhttp.status == 200){
var dbData = xmlhttp.responseText;
if(dbData != ''){
processEvents(dbData); //function for data processing
}
}
getEventdataFromDB(); //run the next request
}
}
}
function getEventdataFromDB(){
xmlhttp.open('GET','getEvents.php?dataRequest=true',true);
xmlhttp.send();
}
If you wanted a little delay between the requests, you could wrap the call to the next request inside a timeout.

Ajax & JavaScript | Limiting requests triggered by user

I am currently playing around with some Ajax code. I have come up with this scenario to try and mirror my problem to see if you, experts, can present a solution, thanks.
Scenario:
I have a HTML button like so: <p onclick="ajax_call();">Click</p>. Upon clicking this button it will launch an AJAX request to a php page like this:
function ajax_launch(){
xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = ajax_launch_callback;
xmlhttp.open("POST", "/php_script", true);
xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xmlhttp.send();
}
function ajax_launch_callback(){
if(xmlhttp.readyState==4 && xmlhttp.status==200){
// code to do something once response is here successfulyl
}
}
This then does some PHP code in the php_script file and returns an $output
Issue:
The php_script page that is called via AJAX is quiet heavy and makes several API and database calls making the page "slow" to load (which is perfectly fine). However at the moment, whilst the page is waiting for a response (it is still doing the php and not yet returned anything) a user can technically spam the button to launch many ajax calls. Ideally, this will just produce stress on the server and I need a way that once the request is pending and not come back, you cannot make further requests.
How can i achieve something like this?
Thanks in advance, looking forward for your solutions/consultation
ALSO:
By multiple requests, this is what i mean - see picture of when i spam click the button to launch several requests whilst the first one isn't done (not returned anything yet):
Image of chrome debugger (networks tab)
Although the mentioned javascript solutions here and in the linked question are a nice addition, you should really do this server-side as a spammer would not necessarily be using a browser and / or could have javascript disabled.
If you use sessions on the server, the session will be locked when a request is being processed so you will only process one request per user at a time. However, requests could queue up (that is perhaps what is showing in your networks tab data?) so you could complement that with a rate limit on for example the IP address.
You can try this:
var xmlhttp;
function ajax_launch() {
if (xmlhttp && xmlhttp.readyState == 4 || !xmlhttp) {
xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = ajax_launch_callback;
xmlhttp.open("POST", "/php_script", true);
xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xmlhttp.send();
}
}
function ajax_launch_callback() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
// code to do something once response is here successfulyl
}
}
This things might be helpful:
Add this HTML
<div style="display: none;" id="screenblocker"> </div>
And this styles:
#screenblocker {
position:absolute;
left:0px;
top:0px;
right:0px;
bottom:0px;
background-color:#ffffff;
opacity:0.4;
filter:alpha(opacity=40);
z-index:9999999;
}
And script part:After the AJAX call
var e = document.getElementById('screenblocker');
if (e != null) {
e.style.display = 'block';
setTimeout("document.getElementById('screenblocker').style.display = 'none';", 5000);//considering 5 seconds to load, you can block longer if needed
}
And on AJAX success:
document.getElementById('screenblocker').style.display = 'none';

difference from using ajax from jquery and javascript

i cant find an answer to this
i have been learning ajax lately but i learned how to do it all in javascript
now i go swiming around ajax question here and almost all are using jquery
so i end up confused. should i use normal javascript or do it through jquery?
so what are the differences?
this is my normal approach
var xmlHttp = createXmlHttpRequestObject(); //you first create the object to this function global
function createXmlHttpRequestObject() //here you instruct the function
{
var xmlHttp; //here you tell it to use the local variable not the global version of it because this returns to the global with the propper values
if (window.XMLHttpRequest) //if the "window" or browser is aware of this Object 90% of browsers
{
xmlHttp = new XMLHttpRequest(); // if true then the variable is now equal to the heart of ajax
}
else //for internet explorer
{
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
}
return xmlHttp; //we return it back to daddy the global variable this objects does everything
//this object is everything in ajax for the most part
}
function process() //function that gets called on load of the body
{
if(xmlHttp) //if its not void or if you can run ajax
{
try //try catch statement are sort of required for the heart of things like ajax and server communication
{
xmlHttp.open("GET", "bacon.txt", true); //here 1st type of call 2nd from where 3rd
//is it true assyscronly or at the same time
//it does not start the connection to the server, its only sets up settings for the connection
xmlHttp.onreadystatechange = handleServerResponse; //anytime something changes[propertie]
//i want to call that function handleserverresponse
//begin communication
xmlHttp.send(null); //this is what communicates to the server makesure on ready statechane before this
//if the server responds you want to make sure everything is taking care of like what function onready state change is it going to call
//or what to do like open a text file in this case
} catch(e)
{
alert( e.toString() ); //alert the error message as a string on a popup
}
}
}
//handling the server response
function handleServerResponse()
{
theD = document.getElementById('theD'); //get the div in a var
if(xmlHttp.readyState==1) //if the connection to the server is established
{ //google crhome may not show this one, some browsers ignore some states
theD.innerHTML += "Status 1: server connection established<br>";
}
else if(xmlHttp.readyState==2) //request received, hey client i am the server and i received your request
{
theD.innerHTML += "Status 2: request reveived <br>";
}
else if(xmlHttp.readyState==3) //while is doing its thing
{
theD.innerHTML += "Status 3: processing request <br>";
}
else if(xmlHttp.readyState==4) //your request is finished and ready
{ //it means your response is ready but doesnt guaranteed no trouble
if(xmlHttp.status==200) //if the status is 200 then is succesufll
{
//IF everthing finally good THE GOOD PART
try //put all your calls on a try statement REMEMBER
{
//get the txt file as a string
text = xmlHttp.responseText; //response text n a normal string
theD.innerHTML += "Status 4: request is finished and response is finished";
theD.innerHTML += text;
}catch (e)
{
alert( e.toString() );
}
} else //for the other statuses like 404 else somehting went wrong
{
alert( xmlHttp.statusText ); //this will give you a status report on the wrong
}
}
}
jQuery simply wraps all of the XmlHttpRequest calls into a simple to use library. In the guts of jQuery you will see code that creates the XmlHttpRequest objects.
You can see the code that does this here:
https://github.com/jquery/jquery/blob/master/src/ajax/xhr.js
The nice thing about using a framework like jQuery is that they handle a lot of the browser idiosyncrasies for you. They also handle a lot of the edge cases you might not think about when writing your code.
Using jQuery is just for ease of use. If you prefer doing it the javascript way then carry on doing that.
jQuery ajax calls do exactly the same as what you are doing but jQuery is a javascript framework which simplifies what you write.
jQuery is a javascript framework which contains library of simplified functions compared to the Native Javascript. One of the functions is the XMLHttpRequest.
This way we just need to implement functions that is needed to implement without needed to write the traditional codes to setup the AJAX system to work
You may learn more about jQuery ajax here:
http://api.jquery.com/jQuery.ajax/

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