Javascript (Vanilla) Execution Order inside Function with DOM update and AJAX call - javascript

I've a piece of JavaScript that gets called when a button is clicked.
The function manipulates some DOM (makes the page opaque, reveals an animation) and then loops through AJAX calls to 'script.php' many times. When that's over it reloads the page, fresh.
My issues is that on Chrome and IE, for the life of me, I can't get the DOM modifications to happen before the AJAX runs and completes. It works fine on Firefox.
I've tried calling sequentially in the code. Nesting each part in a function and in a third, calling each in order.
I've tried to use a promise. Everytime, the AJAX runs and completes, the screen flickers with the DOM mods just before the page reloads.
How is this usually managed?
The problem in a nutshell.
What are some ways I can force Chrome (and Edge) to update the DOM first and then run the AJAX rather than the other way around.
My code is here:
function Backup(OrgRowIDs) {
document.getElementById('Overlay').style.visibility="visible";
document.getElementById('Overlay').style.display="block";
document.getElementById('ActionWindow').style.visibility="visible";
document.getElementById('ActionWindow').style.display="inline";
function GenerateBackupID () {
var Characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
var ID = '';
for (var i = 1; i <= 12; i++) {
var pos = Math.floor((Math.random() * 60) + 1)
ID = ID + Characters.substring(pos, pos + 1);
}
return ID;
}
function RecordBackupSessionID() {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
return;
}
}
xhttp.open("GET", "../AJAX/RecordBackupID?backupfile=" + OrgRowIDs[i] + "&backupid=" + BID , false);
xhttp.send();
}
for (i = 0; i < OrgRowIDs.length; i++){
BID = GenerateBackupID();
RecordBackupSessionID();
function BackupFiles(i) {
for (j = 0; j < 20; j++) {
function BackupEndPoints(j) {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
//do something if we want.
}
}
xhttp.open("GET", "../AJAX/BackupFiles?backupfile=" + OrgRowIDs[i] + "&endpoint=" + j + "&backupid=" + BID , false);
xhttp.send();
}
BackupEndPoints(j)
}
}
BackupFiles(i)
}
location.reload();
}

Related

How do I convert this code block into a Javascript loop?

This function operates perfectly, onclick it subtracts a price amount from 7 ‘cosT’ divs and 1 ‘cosT1’ div, as if removing an item from a shopping cart.
function changePrice0000() {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
var x = document.querySelectorAll(".cosT, .cosT1");
x[0].innerHTML = parseFloat(Number(x[0].innerHTML) - Number(x[7].innerHTML)).toFixed(2);
x[1].innerHTML = parseFloat(Number(x[1].innerHTML) - Number(x[7].innerHTML)).toFixed(2);
x[2].innerHTML = parseFloat(Number(x[2].innerHTML) - Number(x[7].innerHTML)).toFixed(2);
x[3].innerHTML = parseFloat(Number(x[3].innerHTML) - Number(x[7].innerHTML)).toFixed(2);
x[4].innerHTML = parseFloat(Number(x[4].innerHTML) - Number(x[7].innerHTML)).toFixed(2);
x[5].innerHTML = parseFloat(Number(x[5].innerHTML) - Number(x[7].innerHTML)).toFixed(2);
x[6].innerHTML = parseFloat(Number(x[6].innerHTML) - Number(x[7].innerHTML)).toFixed(2);
x[7].innerHTML = parseFloat(Number(x[7].innerHTML) - Number(x[7].innerHTML)).toFixed(2);
}
};
xhttp.open("GET", "text/p0000.txt", true);
xhttp.send();
}
I’ve tried a few variations of the following, nooby attempts at looping and getting it to work but without even remote success…
function changePrice0000() {
for(i=0; i<7; i++) {
var xhttp = new XMLHttpRequest();xhttp.onreadystatechange = function() {if (this.readyState == 4 && this.status == 200) {
var x = document.querySelectorAll(".cosT, .cosT1");
x[n].innerHTML = parseFloat(Number(x[n].innerHTML) - Number(x[7].innerHTML)).toFixed(2);
x[0].innerHTML = parseFloat(Number(x[0].innerHTML) - Number(x[7].innerHTML)).toFixed(2);}};
xhttp.open("GET", "text/p0000.txt", true);
xhttp.send();
}
}
…way beyond my capabilities , one for the experts I think, explicit assistance or just a point in the right direction would be most gratefully appreciated.
You were close, you only need the for loop around the part of the code you want to repeat. You also used an undefined variable n instead of the i in the loop, as #Rup mentioned in the comments:
function changePrice0000() {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
var x = document.querySelectorAll(".cosT, .cosT1");
for(i = 0; i<7; i++) {
x[i].innerHTML = parseFloat(Number(x[i].innerHTML) - Number(x[7].innerHTML)).toFixed(2);
}
xhttp.open("GET", "text/p0000.txt", true);
xhttp.send();
}
}
}
Here a small snippet with a simple loop solution:
var x = document.querySelectorAll(".cost");
x.forEach(function(el) {
el.innerHTML = parseFloat(Number(el.innerHTML) - Number(x[7].innerHTML)).toFixed(2);
});
<div class="cost">1</div>
<div class="cost">2</div>
<div class="cost">3</div>
<div class="cost">4</div>
<div class="cost">5</div>
<div class="cost">6</div>
<div class="cost">7</div>
<div class="cost">8</div>
function changePrice0000() {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
var x = document.querySelectorAll(".cosT, .cosT1");
// loop over n starts here
for (n = 0; n < 7; n++) {
x[n].innerHTML = parseFloat(
Number(x[n].innerHTML) - Number(x[7].innerHTML)
).toFixed(2);
}
// loop ends here
}
xhttp.open("GET", "text/p0000.txt", true);
xhttp.send();
};
}
maybe this is what you mean? N is the var, so I from your example will not work, and you gotta makes sure your loop is inside of where you want to handle it...
Ok so huge thank you to the blisteringly fast expert responses, bit of jigging about–here is what is working perfectly (don’t ask me how :), endeavoured to indent a bit:
function changePrice0000() {var xhttp = new XMLHttpRequest();xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
var x = document.querySelectorAll(".cosT, .cosT1");
// loop begins
for(n=0; n<7; n++) {
x[n].innerHTML = parseFloat(Number(x[n].innerHTML) - Number(x[7].innerHTML)).toFixed(2)};
// loop ends
x[n].innerHTML = parseFloat(Number(x[n].innerHTML) - Number(x[7].innerHTML)).toFixed(2);}};
xhttp.open("GET", "text/p0000.txt", true);
xhttp.send();}
Thanks again guys !!!
You just have to put for loop to the code which you want to repeat.
var n=7
for(i = 0; i <= n; i++) {
x[i].innerHTML = parseFloat(Number(x[i].innerHTML)-(Number(x[n].innerHTML)).toFixed(2);
}

Limit number of concurrent asynchronous xhr requests in javascript - avoid net::ERR_INSUFFICIENT_RESOURCES

I wrote a function to make multiple asynchronous xhr requests to a same domain url and seeing net::ERR_INSUFFICIENT_RESOURCES error in chrome very often. I believe this is because of huge number of requests I'm making, around ~4000.
I need to wait for all the requests to complete and then call a function(func1) passing the results, here is my function
function doRequests(totalEntities)
{
var count = 0;
var request = new Array(totalEntities);
var result = new Array(totalEntities);
for (var i = 0; i < totalEntities; i++) {
(function(i) {
request[i] = new XMLHttpRequest();
request[i].open("GET", uri+"/"+i, true);
request[i].onreadystatechange = function() {
if (request[i].readyState == XMLHttpRequest.DONE) {
if (200 <= request[i].status < 300) {
result[i] = {
data: JSON.parse(request[i].responseText)
}
} else {
console.log(request[i].statusText);
}
count++;
if (count > (totalEntities) - 1) func1(result);
}
};
request[i].send();
}
}(i));
}
}
How can I get rid of ERR_INSUFFICIENT_RESOURCES error? How can I make say 10 requests at a time and then wait for them to complete before sending next batch of 10 and so on...?

Images after javascript are showed with a delay and reordering

I get the src code for image from PHP and after show at the HTML but the images are shown with a delay. How can I show the images all together without reordering after all loaded?
HTML
<div id="keys"></div>
JS
function sendGETDataToServer() {
// Set up our HTTP request
var xhr = new XMLHttpRequest();
// Setup our listener to process completed requests
xhr.onreadystatechange = function () {
// Only run if the request is complete
if (xhr.readyState !== 4) return;
// Process our return data
if (xhr.status >= 200 && xhr.status < 300) {
var JsonResponse = xhr.responseText;
var response = JSON.parse(JsonResponse);
console.log(response);
if (response[0] == "200 OK") {
var i;
for (i = 0; i < response[1]; i++) {
let img = document.createElement("img");
img.src = response[2 + i].FilePath;
img.setAttribute("style", "width:8%; padding-left:1em; float:left");
$("#keys").append(img);
}
}
} else {
console.log("error", xhr);
}
};
xhr.open("GET", "load.php");
xhr.send();
}
Your code should look like something like that
if (response[0] == '200 OK') {
var i;
var nbLoaded = 0;
var nbToLoad = 0;
var pendingImages = [];
for (i = 0; i < response[1]; i++) {
nbToLoad++;
let img = document.createElement('img');
img.src = response[2 + i].FilePath;
img.setAttribute('style', 'width:8%; padding-left:1em; float:left');
img.onload = () => {
nbLoaded++;
if (nbLoaded === nbToLoad) {
pendingImages.forEach((image) => {
$('#keys').append(image);
});
}
};
pendingImages.push(img);
}
}
The short answer to your problem: There isn't really one. There is not a way to know what order your images will arrive in, and you certainly cannot control the delay. What you can control is the order you show them in. The simplest way to do this is to wait until you have received all of your images, and while you are receiveing them, you store them in an array somewhere. After you receive the final image, you could simply order the array the way you want your images to be displayed, and run through it to actually put the images on the page. If you need any help with the specifics, please feel free to ask!

Detect scroll to bottom of div and load new posts

I am using this method to detect the end of two div columns and load new posts. It is working. But the problem is, first it loads five posts by default function, then when user scroll down it loads the next five posts(2nd phase) at least 10 times. than when you scroll more down, it loads next 5 posts(3rd phase) again at least 10 times. Otherwise the function is good.
<script>
$(window).scroll(function() {
if($(window).scrollTop() >= $('.div1').height() - $(window).height()) {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
$('#loadmoreinstagram').remove();
document.getElementById("instagramresponse").innerHTML = document.getElementById("instagramresponse").innerHTML+this.responseText;
}
};
xmlhttp.open("GET", "<?php echo $settings['URL']; ?>getdata.php?type=" + $('#type').last().val() + "&page=" + $('#page').last().val() + "&lasttime=" + $('#lasttime').last().val(), true);
xmlhttp.send();
}
if($(window).scrollTop() >= $('.div2').height() - $(window).height()) {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
$('#loadmoretwitter').remove();
document.getElementById("twitterresponse").innerHTML = document.getElementById("twitterresponse").innerHTML+this.responseText;
}
};
xmlhttp.open("GET", "<?php echo $settings['URL']; ?>getdata.php?type=" + $('#typetwitter').last().val() + "&page=" + $('#pagetwitter').last().val() + "&lasttime=" + $('#lasttimetwitter').last().val(), true);
xmlhttp.send();
}
});
</script>
Your problem is that the window.scroll event fires many times for one "scroll". you can see this by simply logging the events by pasting the following into your console.
$(window).scroll(function() {console.log('scroll')})
To fix this, you need to block your request from firing many times in a row in a short period of time. This is called debouncing. Underscorejs has a great builtin function for it, but a quick google will yield lots of resources for writing your own. Code below using the underscorejs function solves your problem.
var debouncedFn = _.debounce(function() { console.log('scroll')}, 100)
$(window).scroll(debouncedFn)
Learning about debounce https://davidwalsh.name/javascript-debounce-function
for your application:
</script>
function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};
function handleScroll() {
if($(window).scrollTop() >= $('.div1').height() - $(window).height()) {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
$('#loadmoreinstagram').remove();
document.getElementById("instagramresponse").innerHTML = document.getElementById("instagramresponse").innerHTML+this.responseText;
}
};
xmlhttp.open("GET", "<?php echo $settings['URL']; ?>getdata.php?type=" + $('#type').last().val() + "&page=" + $('#page').last().val() + "&lasttime=" + $('#lasttime').last().val(), true);
xmlhttp.send();
}
if($(window).scrollTop() >= $('.div2').height() - $(window).height()) {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
$('#loadmoretwitter').remove();
document.getElementById("twitterresponse").innerHTML = document.getElementById("twitterresponse").innerHTML+this.responseText;
}
};
xmlhttp.open("GET", "<?php echo $settings['URL']; ?>getdata.php?type=" + $('#typetwitter').last().val() + "&page=" + $('#pagetwitter').last().val() + "&lasttime=" + $('#lasttimetwitter').last().val(), true);
xmlhttp.send();
}
}
var debouncedScroll = debounce(handleScroll, 100)
$(window).scroll(debouncedScroll);
</script>
Typically, if you're using jquery, you do something like this:
$.get(url).done( result => {
// do stuff when ajax is done
})
but this setup you have here seems to rely solely on the users' scrolling action to activate the stuff to do in done. So I'm not sure what to tell you other than send it back to the developer (according to a comment you made elsewhere).
A regular implementation of jquery would be something like:
<script>
var working = false;
$(window).scroll(function() {
if($(window).scrollTop() >= $('.div1').height() - $(window).height() && !working) {
working = true;
$.get( url ).done( result => {
$('#loadmoreinstagram').remove();
$("#loadmoreinstagram").html = $("#loadmoreinstagram").html + result.responseText;
working = false;
} )
}
if($(window).scrollTop() >= $('.div2').height() - $(window).height() && !working) {
working = true;
$.get( url ).done( result => {
$('#loadmoretwitter').remove();
$("#loadmoretwitter").html = $("#loadmoretwitter").html + result.responseText;
working = false;
} )
}
});
</script>
You can see the differences, that your dev isn't using jquery to make the hard stuff easy.

Storing the value of variable in JS

Since my main language is C, I am used to pointers and I love them. Now I have some project which I need to finish in Javascript and I've got a problem which I don't know how to solve.
I want to store the value of a variable which I got from GET request. I have a script to send GET to PHP page, which then sends GET to my daemon written in C. When I get the string I wanted, I use length to measure the size of the string I got and in next GET request I want to send that number of bytes I got as the URL parameter.
window.onload = function() {
if (bytes === undefined) {
var bytes = 0;
}
var url = "/test/log.php?q=" + bytes;
function httpGet(url) {
var xhttp = new XMLHttpRequest();
xhttp.open("GET", url, true);
xhttp.onload = function(e) {
if (xhttp.readyState === 4) {
if (xhttp.status === 200) {
console.log(xhttp.responseText);
var option = "";
obj = JSON.parse(xhttp.responseText);
for (var key in obj) {
option += obj[key];
}
document.getElementById("list").innerHTML = asdf;
bytes = option.length;
}
};
xhttp.onerror = function(e) {
console.error(xhttp.statusText);
}
};
xhttp.send();
}
var updateInterval = 2000;
function update() {
httpGet(url);
setTimeout(update, updateInterval);
}
update();
}
So, the focus is on the variable bytes. It should have the value 0 when the script is a first time called, and after every loop (it loops every 2 seconds, I didn't show the loop in the code) it should have the value of the previous length of received string.
You just need to make sure to add the bytes param onto your url in a way that changes with each call rather than just once at page load when it will always be 0.
window.onload = function() {
if (bytes === undefined) {
var bytes = 0;
}
var url = "/test/log.php?q=";
function httpGet(url) {
var xhttp = new XMLHttpRequest();
xhttp.open("GET", url, true);
xhttp.onload = function(e) {
if (xhttp.readyState === 4) {
if (xhttp.status === 200) {
console.log(xhttp.responseText);
var option = "";
obj = JSON.parse(xhttp.responseText);
for (var key in obj) {
option += obj[key];
}
document.getElementById("list").innerHTML = asdf;
bytes = option.length;
}
};
xhttp.onerror = function(e) {
console.error(xhttp.statusText);
}
};
xhttp.send();
}
var updateInterval = 2000;
function update() {
httpGet(url + bytes);
setTimeout(update, updateInterval);
}
update();
}
Instead of a fixed value of url make it to a function and it will give you always the current Url with the modified version of bytes, if you modify it
You have only to change this parts
var url = ...
// to
function getUrl() {
return "/test/log.php?q=" + bytes;
}
...
// and
xhttp.open("GET", url, true);
// to
xhttp.open("GET", getUrl(), true);
I'd declare the variable in a context that doesn't empty its value when the function is called. So, you can declare your variable "bytes" before the function, and then looping through that function. In this case, the variable will hold the last value until you overwrite it.
That should work!

Categories

Resources