AJAX code order's difference - javascript

I saw most of the AJAX examples in W3school look like this:
function loadDoc() {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (xhttp.readyState == 4 && xhttp.status == 200) {
document.getElementById("demo").innerHTML = xhttp.responseText; //get response
}
};
xhttp.open("GET", "ajax_info.txt", true);
xhttp.send(); //send request
}
I changed the order of the code like this:
function loadDoc() {
var xhttp = new XMLHttpRequest();
xhttp.open("GET", "ajax_info.txt", true);
xhttp.send(); //send request
xhttp.onreadystatechange = function() {
if (xhttp.readyState == 4 && xhttp.status == 200) {
document.getElementById("demo").innerHTML = xhttp.responseText; //get response
}
};
}
and it seems like it works too, and my brain will think second one more logic.
So my question is: Is there any rules on this AJAX code's order? Will it make any differences for the 2 example above in any situation?

The AJAX request is handled asynchronously so the order of definition of the callback does not matter.
I have annotated your code with some notes. The execution order (in this case) will be:
1 -> 2 -> 3 (Request Triggered) -> 4 (Define callback)
|
5 (Callback function called asynchronously every time the request state changes)
|
6 Execute logic if request is successful
Steps 1 (Creation of XHR Object), 2 (Opening request) and 3 (sending request) must be in sequence. Step 4 can happen any time after the creation of the object as long as it's before the network response or state change happens.
Reading up on AJAX and Asynchronous Callbacks in JS will help understand this better. You can start here.
function loadDoc() {
//1. Create XHR Object
var xhttp = new XMLHttpRequest();
//2. Define request parameters
xhttp.open("GET", "ajax_info.txt", true);
//3. Trigger Request
xhttp.send();
//4. Define callback for state change
xhttp.onreadystatechange = function() {
//5. This code is executed every time the state of the request changes
if (xhttp.readyState == 4 && xhttp.status == 200) {
//6. This code executes only if readyState is 4 (request is done) and the http status is 200 (success)
document.getElementById("demo").innerHTML = xhttp.responseText; //get response
}
};
}

Related

How to make callback run asynchronously?

I have a JavaScript function called getAandB which takes a callback. getAandB firstly gets value 'a' using ajax. It then invokes the callback with value 'a' as an argument. The callback gets value 'b' and console.logs both 'a' and 'b' to the console. so I get {"key":"a"} {"key":"b"} in the console.
I thought that the two ajax calls would happen simultaneously / asynchronously. However, they seem to run one after the other ie. synchronously.
The JavaScript code and the PHP code for the ajax requests is shown below:
index.html:
<script>
function getAandB(callback){
const xhr = new XMLHttpRequest();
xhr.open('GET', './ajax-a.php', true);
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
xhr.onreadystatechange = function(){
if(xhr.readyState === 4 && xhr.status === 200){
callback(xhr.responseText)
}
}
xhr.send();
}
function callback(resultA){
const xhr = new XMLHttpRequest();
xhr.open('GET', './ajax-b.php', true);
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
xhr.onreadystatechange = function(){
if(xhr.readyState === 4 && xhr.status === 200){
const resultB = xhr.responseText;
console.log(resultA, resultB);
}
}
xhr.send();
}
getAandB(callback);
</script>
ajax-a.php:
<?php
sleep(5);
$response = [
"key" => "a",
];
echo json_encode($response);
The code for ajax-b.php is the same as for ajax-a.php except the value of $response.key is b not a.
I thought that the above code would result in ajax calls being made simultaneously to get 'a' and 'b'. However if the PHP code sleeps for 5 seconds for both ajax-a.php and ajax-b.php, then it takes 10 seconds for the console.log to appear. If only one of the ajax-?.php scripts sleeps for 5 seconds then it takes 5 seconds for the console.log to appear.
How can I use callbacks to allow me to combine the results of ajax calls, as I have done here, but to make the individual calls happen simultaneously / asynchronously? Alternatively, is not possible to implement this with callbacks?
If you want the request to ajax-b to me made at approximately the same time as the request for ajax-a then you need to make the respective calls to xhr.send() at approximately the same time.
At the moment, the call to ajax-b's send() takes place as part of callback() which you only call after you have received the response to the request for ajax-a.
You then need to add additional logic to determine when you have received both responses so you log both bits of data at the same time (assuming you still want to do that).
A rough and ready way to do that, keeping to your current approach, would look something like this:
function getA(callback){
const xhr = new XMLHttpRequest();
xhr.open('GET', './ajax-a.php', true);
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
xhr.onreadystatechange = function(){
if(xhr.readyState === 4 && xhr.status === 200){
callback(xhr.responseText)
}
}
xhr.send();
}
function getB(callback){
const xhr = new XMLHttpRequest();
xhr.open('GET', './ajax-b.php', true);
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
xhr.onreadystatechange = function(){
if(xhr.readyState === 4 && xhr.status === 200){
const resultB = xhr.responseText;
callback(xhr.responseText)
}
}
xhr.send();
}
function getAandB() {
const data = [];
function callback(responseData) {
data.push(responseData);
if (data.length === 2) {
console.log(...data);
}
}
getA(callback);
getB(callback);
}
getAandB();
We have better tools for that these days though, thanks to promises and modern APIs (like fetch) which support them natively.
async function getAandB() {
const dataPromises = [
fetch("./ajax-a.php").then(r => r.text()),
fetch("./ajax-b.php").then(r => r.text())
];
const data = await Promise.all(dataPromises);
console.log(...data);
}
getAandB();
I tried to edit my question but 'the edit queue was full'.
It took me a while to understand #Quentin's answer but I finally realized it relies on the fact that both instantiations of the callback function are altering the same variable (I think that is called by reference and is the default situation with arrays). Given this, although the instantiations know nothing about each other, it is possible to know when both ajax calls have completed by checking to see if the data array has been updated twice. If it has then both must have completed and data can be consoled out.
There is no need for the getAandB function. This much simpler and less confusing code works exactly the same as Quentin's answer:
<script>
const data = [];
function getA(){
const xhr = new XMLHttpRequest();
xhr.open('GET', './ajax-a.php', true);
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
xhr.onreadystatechange = function(){
if(xhr.readyState === 4 && xhr.status === 200){
data.push(xhr.responseText);
if (data.length === 2){
console.log(...data);
}
}
}
xhr.send();
}
function getB(){
const xhr = new XMLHttpRequest();
xhr.open('GET', './ajax-b.php', true);
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
xhr.onreadystatechange = function(){
if(xhr.readyState === 4 && xhr.status === 200){
data.push(xhr.responseText);
if (data.length === 2){
console.log(...data);
}
}
}
xhr.send();
}
getA();
getB();
</script>

XMLHttpRequest Replace Element Won't Work With LocalStorage

I tried to implement cookie logging with localStorage into the javascript code for replacing a specific element. It uses the XMLHttprequest method and, I got no idea why it won't work with localStorage. Please enlighten me.
localStorage.setItem("replace1", this.JSON.parse(responseText));
function loadDoc() {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("replace1").innerHTML = localStorage.getItem("replace1");
}
};
xhttp.open("GET", "yoinkexecutor2.php", true);
xhttp.send();
}
You can only display data when the async operation (GET) request terminates.
Otherwise you'll get undefined since nothing exits in the localStorage under that key
Also, you can only store strings in local storage, meaning you need to parse that object string once you want to retrieve the data using getItem
function loadDoc() {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
localStorage.setItem("replace1", JSON.stringify(this.responseText))
document.getElementById("replace1").innerHTML = JSON.parse(localStorage.getItem("replace1"))
}
};
xhttp.open("GET", "yoinkexecutor2.php", true);
xhttp.send();
}

Close specific XMLHttpRequest

How to "rebuild" function to close specific XMLHttpRequest? I have defined variable outside function to call xhr.abort(); everywhere I need. Now is possible, with this solution, close last running XMLHttpRequest if running more than one at same time - processes before last running are without control after replace xhr by re-calling _ajax()
var xhr;
function _ajax(data, callback) {
xhr = new XMLHttpRequest();
xhr.open('POST', window.location.pathname, true);
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
callback(this);
}
};
xhr.send(data);
}
/* close fnc */
xhr.abort();
You could use xhr as an array and store there all the requests; then you can call abort on any one of them. Like:
var xhr=[];
function _ajax(data, callback) {
xhr.push(new XMLHpptRequest);
//etc
}
xhr[0].abort();
xhr.shift(); //get rid of the aborted request

Call MVC Controller Method from TypeScript/JavaScript without jquery

Is it possible to call MVC Controller Method with Ajax call in Type Script/JavaScript without Jquery? if not How I can Call Controller Method from JavaScript/Type Script file?
Consider a method that is calling Controller method to sort and send a sort column to it:
This is function in ts file:
function SortData()
{
.... call Controller method and send sortCriteria (FullName) to it
}
and this is Controller method:
[Route("sortbycolumn")]
public ActionResult SortByColumn(string sortCriteria)
{
.... Do the sort retun back json result
}
Of course you can. In fact, jQuery is library based on javascript and it is not an independent language. Here is what you have to do:
function SortData(){
// Every ajax call is an XMLHttpRequest
var xhttp = new XMLHttpRequest();
// It means that your request is processed asynchronously.
// So we need to define the method that has to be run once the response is received,
xhttp.onreadystatechange = function() {
// status 200 means that your request has been processed successfully.
if (this.readyState == 4 && this.status == 200) {
// Change your html here
}
};
// Setting your request
xhttp.open("POST", "mycontroller/myaction", true);
// Send your request when everything is set.
xhttp.send();
}
If you need more to know, check out this link: https://www.w3schools.com/xml/ajax_intro.asp
I've included an example of a GET and a POST + submitting/receiving data using vanilla JS & AJAX below.
For further info, give this a read.
Good luck.
function SortData() {
var data;
//Post Example
var xhttp = new XMLHttpRequest();
xhttp.open("POST", "/Controller/Action", true);
xhttp.setRequestHeader("Content-Type", "application/json");
//There are two options for using xhttp.send(): Only keep the ONE that applies to you
//Option 1: Submit data to the server
xhttp.send(JSON.stringify(params));
//OR
//Option 2: Nothing to submit to the server
xhttp.send();
xhttp.onload = function(response) {
if(response.target.status == 200) {
data = JSON.parse(response.target.response);
}
};
//Get Example
var xhttp = new XMLHttpRequest();
xhttp.open("GET", "/Controller/Action", true);
xhttp.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
//There are two options for using xhttp.send(): Only keep the ONE that applies to you
//Option 1: Submit data to the server
xhttp.send(JSON.stringify(params));
//OR
//Option 2: Nothing to submit to the server
xhttp.send();
xhttp.onload = function(response) {
if(response.target.status == 200) {
data = JSON.parse(response.target.response);
}
};
}

javascript: wait for a return

I have this problem.
I have a function for example called functionA() that needs the results from another function called functionB().
var globalVar="";
function functionA(){
//...
functionB();
//here i have to use the global variable (that is empty because functionB isn't finished)
}
function functionB(){
//ajax request
globalVar=ajaxRequest.responseText;
}
How can I do to let the functionB finish befor continue with the execution of functionA?
Thanks!
This is the code:
var ingredientiEsistenti="";
function ShowInserisciCommerciale() {
getElementiEsistenti();
JSON.parse(ingredientiEsistenti);
}
function getElementiEsistenti(){
// prendo gli ingredienti esistenti.
var url = "http://127.0.0.1:8080/Tesi/Ingredienti";
var xmlHttp = new XMLHttpRequest();
xmlHttp.open("POST", url, false);
xmlHttp.send(null);
xmlHttp.setRequestHeader("Content-type",
"application/x-www-form-urlencoded");
xmlHttp.onreadystatechange = function() {
if (xmlHttp.readyState == 4) // COMPLETED
{
if (xmlHttp.status == 200) // SUCCESSFUL
{
ingredientiEsistenti = xmlHttp.responseText;
} else {
alert("An error occurred while communicating with login server.");
}
}
};
}
You've got one of many options, that don't require an evil global variable:
Move the code you want to see executed to the onreadystatechange callback of the ajax request, that way, it won't get executed until you received a response
Redefine functionA, so that it takes a parameter that allows you to skip the first bit:
Make the request synchronous, not recommended, though
use a timeout/interval to check the readystate of the request manually (brute-force, not recommended either)
Perhaps there is some worker trickery that could do the trick, too, in your particular case
function functionA(skipDown)
{
skipDown = skipDown || false;
if (skipDown === false)
{
//doStuff
return functionB();//<-- call functionA(true); from the readystatechange callback
}
//this code will only be called if skipDown was passed
}
It is impossible to have a sleep/wait in JavaScript when the call is asynchronous. You need to use a callback pattern to make this action occur.
It is possible to make an XMLHttpRequest synchronous, but that can lead to other problems. It can hang the browser as it blocks all other actions from happening. So if you want to show a loading animation, it most likely will not execute.
You can make your AJAX request synchronous. https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest/Synchronous_and_Asynchronous_Requests
var request = new XMLHttpRequest();
// Last parameter makes it not asnychronous
request.open('GET', 'http://www.mozilla.org/', false);
request.send(null);
// Won't get here until the network call finishes
if (request.status === 200) {
console.log(request.responseText);
}
However, that will block the UI while waiting for the server to respond, which is almost never what you want. In that case, you should use a callback to process results.
Here's an example using a callback without relying on a global variable. You should always run away from those
function ShowInserisciCommerciale( ) {
getElementiEsistenti(function(responseText) {
JSON.parse(responseText);
});
}
function getElementiEsistenti(successCallback){
var url = "http://127.0.0.1:8080/Tesi/Ingredienti";
var xmlHttp = new XMLHttpRequest();
xmlHttp.open("POST", url, false);
xmlHttp.send(null);
xmlHttp.setRequestHeader("Content-type",
"application/x-www-form-urlencoded");
xmlHttp.onreadystatechange = function() {
if (xmlHttp.readyState == 4) // COMPLETED
{
if (xmlHttp.status == 200) // SUCCESSFUL
{
successCallback(xmlHttp.responseText);
} else {
alert("An error occurred while communicating with login server.");
}
}
};
}

Categories

Resources