Related
I several divs on my site, I'd like to update one by one. In order not to spam the server with 200+ requests at once, I'd like to delay them by 1s each.
What I tried:
var $tourBox = $('.tour-box');
$tourBox.each(function () {
var $box = $(this);
setTimeout(function () {
getUpdate($box);
}, 1000);
});
The update function:
function getUpdate($box) {
var $so = $box.attr('data-so');
var $url = $('#ajax-route-object-status').val();
$.get($url, {
so: $so
}).success(function (data) {
var $bg = 'bg-gray';
if (data.extra.isStarted === true && data.extra.isFinished === false) {
$bg = 'bg-orange'
}
if (data.extra.isStarted === true && data.extra.isFinished === true) {
$bg = 'bg-green'
}
if (data.extra.isLate === true && data.extra.isFinished === false) {
$bg = 'bg-red'
}
$box.removeClass('bg-gray').removeClass('bg-green').removeClass('bg-orange').removeClass('bg-red').addClass($bg);
});
}
In Chrome Dev--> Network it shows the all loaded as pending and then loads them one by one, but w/out the delay:
As you can see between 3907 and 3940, there is merely half a second delay. This doesn't change even if I have a timeout of 5000.
Back in 2008 I wrote a slowEach() plugin for jQuery that does what you're looking for. It's basically a drop-in replacement for $.each() and $(...).each() that takes a time interval, so the callback is called with that amount of delay for each element:
jQuery.slowEach = function( array, interval, callback ) {
if( ! array.length ) return;
var i = 0;
next();
function next() {
if( callback.call( array[i], i, array[i] ) !== false ) {
if( ++i < array.length ) {
setTimeout( next, interval );
}
}
}
};
jQuery.fn.slowEach = function( interval, callback ) {
jQuery.slowEach( this, interval, callback );
};
With that code, you can then do:
$('.tour-box').slowEach( 1000, function() {
getUpdate( $(this) );
});
One thing to note about this code is that it uses only a single timer at a time, instead of making hundreds of setTimeout() calls to fire up multiple timers at once. This makes it much easier on system resources.
Your for each is calling all of the timeouts at once. It is not waiting one second in between calling each time out. So you have thousands of objects that are being scheduled to call getUpdate($box); after one second.
What you can do is increase the timeout in each iteration.
var $tourBox = $('.tour-box');
var delay = 1000;
$tourBox.each(function () {
var $box = $(this);
setTimeout(function () {
getUpdate($box);
}, delay);
delay += 1000;
});
This will cause your first timeout to be fired after 1 second, and your second after two seconds and so on.
Calling one by one could be another solution to avoid spam like request.
In that case, this could be a solution:
$(document).ready(function(){
var $tourBox = $('.tour-box'),
curIndex = 0,
totalBox = $tourBox.length,
url = $('#ajax-route-object-status').val(),
function getUpdate(){
var $box = $tourBox.get( curIndex );
if ( typeof $box === 'undefined'){
// No more box to process
return; // exit
}
var $so = $box.attr('data-so');
$.get($url, {
so: $so
}).success(function (data) {
var $bg = 'bg-gray';
if (data.extra.isStarted === true && data.extra.isFinished === false) {
$bg = 'bg-orange'
}
if (data.extra.isStarted === true && data.extra.isFinished === true) {
$bg = 'bg-green'
}
if (data.extra.isLate === true && data.extra.isFinished === false) {
$bg = 'bg-red'
}
$box.removeClass('bg-gray').removeClass('bg-green').removeClass('bg-orange').removeClass('bg-red').addClass($bg);
}).always(function(){
// Increment index to process
curIndex++;
// Finished either with success or failed
// Proceed with next
getUpdate();
});
}
});
I am using google maps and i am trying to put a pause in execution to prevent QUERY_LIMIT usage issue. My function that plots the addresses looks like this.
The code works, however i want to try setTimeout or setInterval to see if its going to look better on UI.
How do i call it, what should be the first argument?
Thanx alot.
vLocations = [];
for (var i = 0; i < vAddresses.length; i++) {
//pause to prevent OVER_QUERY_LIMIT issue
//geocode "free" usage limit is 5 requests per second
//setTimeout(PlotAddressesAsUnAssigned, 1000);
//sleep(500);
//this will resolve the address and store it in vLocations
AddWaypointAndUnassigned(vAddresses[i]);
var z = i % 4;
if (z==0 && i != 0) {
//sleep after every 5th geocode call
//alert('going to sleep...i: ' + i);
//sleep(3000);
}
}
Doing a pause (asynchronous execution) inside a loop (synchronous) will usually result in a lot of trouble.
You can use recursive calls that are done only when a timeout ends.
var vLocations = [];
// Manages the timeout and recursive calls
function AddWaypointAndUnassignedWithPause(index){
setTimeout(function(){
// When the timeout expires, we process the data, and start the next timeout
AddWaypointAndUnassigned(vAddresses[index]);
// Some other code you want to execute
var z = i % 4;
if (z==0 && i != 0) {
//sleep after every 5th geocode call
//alert('going to sleep...i: ' + i);
//sleep(3000);
}
if(index < vAddresses.length-1)
AddWaypointAndUnassignedWithPause(++index);
}, 1000);
}
// Start the loop
AddWaypointAndUnassignedWithPause(0);
JSFiddle example.
Try this, hope this will help
vLocations = [];
for (var i = 0; i < vAddresses.length; i++) {
//pause to prevent OVER_QUERY_LIMIT issue
setTimeout(function(){
//this will resolve the address and store it in vLocations
AddWaypointAndUnassigned(vAddresses[i]);
}, 500);
var z = i % 4;
if (z==0 && i != 0) {
//sleep after every 5th geocode call
//alert('going to sleep...i: ' + i);
//sleep(3000);
}
}
What about a waiting line, thats fired when an item is added and stopped when there are no items left.
With setTimeout:
var INTERVAL = 1000 / 5;
var to = null;
var vLocations = [];
function addAddress(vAddress) {
vLocations.push(vAddress);
startTimeout();
}
function startTimeout() {
if( to === null ) {
to = setTimout(processLocation, INTERVAL);
}
}
function processLocation() {
if( vLocations.length ) {
var vAddress = vLocations.shift();
AddWaypointAndUnassigned(vAddress);
to = setTimout(processLocation, INTERVAL);
} else {
to = null;
}
}
With setInterval:
var INTERVAL = 1000 / 5;
var to = null;
var vLocations = [];
function addAddress(vAddress) {
vLocations.push(vAddress);
startInterval();
}
function startInterval() {
if( to === null ) {
to = setInterval(processLocation, INTERVAL);
}
}
function processLocation(cb) {
if( vLocations.length ) {
var vAddress = vLocations.shift();
AddWaypointAndUnassigned(vAddress);
} else
clearInterval(to);
to = null;
}
}
So I have a loop, which performs an ajax call on each iteration and I want to set the progress bar updated.. But it is not updating, it goes to 100% directly when ending...
I've tried to put the bar update call outside the success action (inside the loop directly) but it isn't working either..
$('button.page').on('click', function(e){
var $userList = textArray($('#page-userlist').val().replace('http://lop/', '').split(/\n/));
var $proxyList = textArray($('#page-proxylist').val().replace('http://', '').split(/\n/));
var $question = $('#page-question').val();
var data = {
question: $question,
users: $userList,
proxies: $proxyList
};
var i = 0, p = 0, max = data.proxies.length, totalusers = data.users.length, percent = 0;
$('#log').append("\n" + moment().calendar() + "\n");
var progressbar = $('#page-progress');
$.each(data.users, function(k, u){
if(typeof(p) !== 'undefined' && p !== null && p > 0)
{
if(i % 10 == 0 && i > 1) p++;
if(p == max) return false;
}
var proxy = data.proxies[p];
percent = Math.round((i / totalusers) * 100);
$.ajax({
type: "POST",
url: Routing.generate('viral_admin_bot_page'),
data: {question: $question, user: u, proxy: proxy},
success: function(result) {
$('#log').append("\nAtacado usuario " + u + " con proxy: " + proxy + "\n");
$(progressbar).width(percent + "%");
},
error: function(error) {
$('#log').append(error);
}
});
i++;
});
});
If i do console.log(percent); it is updating perfectly on each iteration, so I don't know where can be the problem.
Here is my code (without the ajax call because it isn't the problem) http://jsfiddle.net/dvo1dm03/20/
it will output to console the percentage, the objetive is to update the bar to the percentage completed in each loop, so it goes in "realtime" with loop.
Ok, here's how to do it asynchrounously.
var speed = 75;
var number_of_calls_returned = 0; // add number_of_calls_returned++ in your ajax success function
var number_of_total_calls;
var loaded = false;
function processUserData(){
if( number_of_calls_returned < number_of_total_calls){
setTimeout(function(){processUserData();}, 200);
}
else{
//received all data
// set progressbar to 100% width
loaded = true;
$("#page-progress").animate({width: "100%"},500);
$("#page-proxylist").val("Received data");
}
}
function updateProgress(percent, obj){
setTimeout(function(x){
if(!loaded)
$(obj).width(x + "%");
}, percent*speed, percent);
}
$('button.page').on('click', function (e) {
var $userList = textArray($('#page-userlist').val().replace('http://lop/', '').split(/\n/));
var $proxyList = textArray($('#page-proxylist').val().replace('http://', '').split(/\n/));
var $question = $('#page-question').val();
var data = {
question: $question,
users: $userList,
proxies: $proxyList
};
var i = 0,
p = 0,
max = data.proxies.length,
totalusers = data.users.length,
percent = 0;
//$('#log').append("\n" + moment().calendar() + "\n");
var progressbar = $('#page-progress');
number_of_total_calls = totalusers;
$.each(data.users, function (k, u) {
if (typeof (p) !== 'undefined' && p !== null && p > 0) {
if (i % 10 == 0 && i > 1) p++;
if (p == max) return false;
}
var proxy = data.proxies[p];
percent = (i / totalusers) * 100; //much smoother if not int
updateProgress(percent, progressbar);
i++;
// simulate ajax call
setTimeout(function(){number_of_calls_returned++;}, Math.random()*2000);
});
//callback function
setTimeout(function(){processUserData();}, 200);
});
var textArray = function (lines) {
var texts = []
for (var i = 0; i < lines.length; i++) {
// only push this line if it contains a non whitespace character.
if (/\S/.test(lines[i])) {
texts.push($.trim(lines[i]));
}
}
return texts;
}
Check it out here! jsFiddle (really cool!)
Your problem is cause by the fact that you have a closure for your success function and every success function shares the same percent variable. You can fix it like this:
success: function(percent, result) {
$('#log').append("\nAtacado usuario " + u + " con proxy: " + proxy + "\n");
$(progressbar).width(percent + "%");
}.bind(percent),
Where you'll need to shim bind in older browsers, or like this, which is a little uglier, but should work everywhere without a shim:
success: (function(percent) { return function(result) {
$('#log').append("\nAtacado usuario " + u + " con proxy: " + proxy + "\n");
$(progressbar).width(percent + "%");
}; }( percent ),
if what you want is to increase the update bar with each success of AJAX calls I'd suggest an easier solution (I've simplified the js code for clarity's sake):
$('button').click(function (e) {
var i = 0,
cont = 0,
totalusers = 100,
percent = 0;
var progressbar = $('#page-progress');
for (; i < totalusers; i++) {
$.ajax({
type: "POST",
url: '/echo/json/',
data: {
question: 'something',
user: 1,
proxy: 2
},
success: function (result) {
cont += 1;
percent = Math.round((cont / totalusers) * 100);
progressbar.width(percent + "%");
},
error: function (error) {
$('#log').append(error);
}
});
};
});
You can see it in action in this fiddle.
Hope this helps or at least give you some ideas.
Update the progress bar using setTimeout method.
it will wait for some time and then update the width of progressbar.
myVar = setTimeout("javascript function",milliseconds);
Thanks,
Ganesh Shirsat
I would like to make a recommendation of trying to make a self contained example that doesn't rely on the post so that it is easier for you or us to solve the problem
As well, you can console log elements so you could try logging the progressbar element, percent and the response of the ajax request
(This code is to replace the javascript sections of the fiddler)
var i = 0;
moveProgress();
function moveProgress(){
if(i < 10000)
{
setTimeout(function(){
$('#page-progress').width((i / 1000) * 100);
moveProgress();
},2);
i++;
}
}
The reason that it wasn't working was because the loop ran so fast that it was loaded by the time the script loaded it, the timeout allows you to delay the execution a bit(Though not necessarily recommended to use because of potential threading issues.
The following piece of code loads the next page, when the user scrolls to the bottom. However, sometimes it is repeating itself — when the user scrolls too rapidly, or scrolls whilst the AJAX is still being loaded.
Is there a way to prevent it from firing multiple times? So for example, nothing can be loaded while the AJAX is being called, or the AJAX can only be called once a second?
Any help would be great.
$(window).scroll(function() {
if( $(window).scrollTop() + $(window).height() == $(document).height()) {
if (firstURL !== null) {
$.get(firstURL, function(html) { // this gets called multiple times on erratic scrolling
firstURL = '';
var q = $(html).find('.post');
l = $(html).filter('div.bottom-nav');
if( l[0].childNodes.length > 0 ){
firstURL = l[0].children[0].getAttribute('href');
} else {
firstURL = null;
}
q.imagesLoaded( function() {
jQuery(".content").append(q).masonry( 'appended', q, true );
});
});
}
}
});
Just add a flag :
var ready = true; //Assign the flag here
$(window).scroll(function() {
//Check the flag here. Check it first, it's better performance wise.
if(ready && $(window).scrollTop() + $(window).height() == $(document).height()) {
ready = false; //Set the flag here
if (firstURL !== null) {
$.get(firstURL, function(html) { // this gets called multiple times on erratic scrolling
firstURL = '';
var q = $(html).find('.post');
l = $(html).filter('div.bottom-nav');
if( l[0].childNodes.length > 0 ){
firstURL = l[0].children[0].getAttribute('href');
} else {
firstURL = null;
}
q.imagesLoaded( function() {
jQuery(".content").append(q).masonry( 'appended', q, true );
});
}).always(function(){
ready = true; //Reset the flag here
});
}
}
});
I had a similar issue, that scrolling the window fired my function multiple times (manupulating my img slider's properties). To effectively deal with that matter you can defer the execution of scroll handler and use an additional 'page is being scrolled' flag to prevent multiple handler calls.
Check out the example below, you can surely addopt the approach to your case.
$(function()
{
var pageFold = 175; //scrolling threshold
var doScroll = false; //init
var timeoutScroll = 100; //delay
var windowScrolled = false; //initial scrolling indicatior
var windowScrolling = false; //current scrolling status indicator
//load next page handler
function loadNextPage()
{
if(windowScrolling != true)
{
//and do ajax stuff - your code
}
}
//check if page scrolled below threshold handler
function foldedBelow()
{
//nice scrolled px amount detection
return (Math.max($('body').scrollTop(), $('html').scrollTop()) > pageFold);
}
//actual scrolled handler
function doWindowScroll()
{
windowScrolled = true;
if(foldedBelow())
{
loadNextPage();
}
windowScrolling = false;
}
//deffered scroll hook
$(window).scroll(function(e){
windowScrolling = true;
clearTimeout(doScroll);
doScroll = setTimeout(doWindowScroll, timeoutScroll);
});
});
When I did something like this I implemented a timed scroll handler that calls a custom scrolled_to_bottom-event.
(function($, window, document){
"use strict";
var $document = $(document);
var $window = $(window);
var _throttleTimer = null;
var _throttleDelay = 100;
function ScrollHandler(event) {
//throttle event:
clearTimeout(_throttleTimer);
_throttleTimer = setTimeout(function () {
if ($window.scrollTop() + $window.height() > $document.height() - 400) {
console.log('fire_scrolled_to_bottom');
$document.trigger('scrolled_to_bottom');
}
}, _throttleDelay);
}
$document.ready(function () {
$window
.off('scroll', ScrollHandler)
.on('scroll', ScrollHandler);
});
}(jQuery, window, document));
And then in my object handling the reload I bound that event with a flag-check if it was already loading.
handler = {
...,
isLoading: false,
bind: {
var self = this;
$document.on('scrolled_to_bottom', function () {
if (self.isLoading) {
return;
}
self.nextPage();
});
}
nextPage(): function () {
var self = this;
this.isLoading = true;
$.ajax({
url: url,
data: self.searchData,
dataType: "json",
type: "POST",
success: function (json) {
// do what you want with respone
},
error: function (xhr, statusText, errorThrown) {
bootbox.alert('An error occured.');
},
complete: function () {
self.isLoading = false;
}
});
},
init: function () {
this.doInitStuff();
this.bind();
}
}
This way I seperated the concerns and can reuse the triggering nicely, and easily add functionality if other things should happen on reload.
Try storing some kind of data that stores whether the page is currently loading new items. Maybe like this:
$(window).data('ajaxready', true).scroll(function(e) {
if ($(window).data('ajaxready') == false) return;
if ($(window).scrollTop() >= ($(document).height() - $(window).height())) {
$('div#loadmoreajaxloader').show();
$(window).data('ajaxready', false);
$.ajax({
cache: false,
url: 'loadmore.php?lastid=' + $('.postitem:last').attr('id'),
success: function(html) {
if (html) {
$('#postswrapper').append(html);
$('div#loadmoreajaxloader').hide();
} else {
$('div#loadmoreajaxloader').html();
}
$(window).data('ajaxready', true);
}
});
}
});
Right before the Ajax request is sent, a flag is cleared signifying that the document is not ready for more Ajax requests. Once the Ajax completes successfully, it sets the flag back to true, and more requests can be triggered.
copied : jQuery Infinite Scroll - event fires multiple times when scrolling is fast
Here is my solution. You can get an idea and apply it to yours. Also to help others.
You can execute your method first with condition: if(loadInterval ===
null). That means if we already waited for 5 secs.
Assign loadInterval = setTimeout(), then nullify the variable after 5 secs.
Here is sample code.
//declare outside
var loadInterval = null;
// .....
// .....
$(window).scroll(function() {
if ($('.loadmore').isOnScreen() === true) {
//No waiting registered, we can run loadMore
if(loadInterval === null) {
// This console.log executes in 5 seconds interval
console.log('Just called ' + new Date());
// your code in here is prevented from running many times on scroll
// Register setTimeout() to wait for some seconds.
// The code above will not run until this is nullified
loadInterval = setTimeout(function(){
//Nullified interval after 5 seconds
loadInterval = null;}
, 5000);
}
}
});
I post here the IsOnScreen() plugin for jQuery (i found it on stackoverflow :)
$.fn.isOnScreen = function() {
var win = $(window);
var viewport = {
top: win.scrollTop(),
left: win.scrollLeft()
};
viewport.right = viewport.left + win.width();
viewport.bottom = viewport.top + win.height();
var bounds = this.offset();
bounds.right = bounds.left + this.outerWidth();
bounds.bottom = bounds.top + this.outerHeight();
return (!(viewport.right < bounds.left || viewport.left > bounds.right || viewport.bottom < bounds.top || viewport.top > bounds.bottom));
};
for(var x=0 ; x<=23 ; x++)
{
AjaxRequest16 = null;
AjaxRequest16 = getXmlHttpRequestObject(); // method here to load the request
if(AjaxRequest16.readyState == 4 || AjaxRequest16.readyState == 0)
{
AjaxRequest16.open("GET", "ajax.php?id=16&AreaID=" +encodeURIComponent(AreaID)+ "&month="
+encodeURIComponent(document.getElementById("cboMonths").value)+ "&TimeSlot=" +encodeURIComponent(x), true);
AjaxRequest16.send(null);
AjaxRequest16.onreadystatechange = function()
{
if(AjaxRequest16.readyState == 4)
{
var innerHTML = AjaxRequest16.responseText.toString();
/* Retrieve data from the server and display. */
document.getElementById("divTime"+x).innerHTML = innerHTML;
}/* end if */
}/* end function */
}/* end if */
}/* end if */
I'm trying to call ajax multiple times to load data in a set of divs: 24 of them, they start with divTime0, divTime1, divTime2, divTime3...... divTime23. Each time its called, the value for the TimeSlot corresponds with the div e.g. TimeSlot=0 goes in divTime0.
I know the ajax calls here are overriding each other but have no idea how to solve it without writing out 24 blocks of code to get it working. N.B. this is working if i execute singularly without the for loop but it will just populate 1 of the 24 divs
The following code worked to load 24 divs with images:
for(var x=0 ; x<=23 ; x++)
document.getElementById("timeanalysisimg"+x).src="ajax.php?id=15&AreaID=" +encodeURIComponent(AreaID);
I'm trying to do something similar without having to write unnecessary code. Any ideas?
I got it working. Just pasting the solution
for(var x=0 ; x<=9 ; x++)
{
test(x, AreaID); // calling the function which resides externally to the loop
}
An external method:
function test(x, AreaID)
{
var AjaxRequest16 = null;
AjaxRequest16 = getXmlHttpRequestObject();
if(AjaxRequest16.readyState == 4 || AjaxRequest16.readyState == 0)
{
AjaxRequest16.open("GET", "ajax.php?id=16&AreaID=" +encodeURIComponent(AreaID)+ "&month="
+encodeURIComponent(document.getElementById("cboMonths").value)+ "&TimeSlot=" +encodeURIComponent(x), true);
AjaxRequest16.send(null);
AjaxRequest16.onreadystatechange = function()
{
if(AjaxRequest16.readyState == 4)
{
var innerHTML = AjaxRequest16.responseText.toString();
/* Retrieve data from the server and display. */
document.getElementById("divTime"+x).innerHTML = innerHTML;
}
}
}
}
Put the block into a function:
for(var x=0 ; x<=23 ; x++)
{
(function(x) {
var AjaxRequest16 = getXmlHttpRequestObject();
//rest of the code
}(x));
} //end of for loop
you can do something like:
for(var x=0 ; x<=23 ; x++)
{
req(x);
}
function req(x){
var AjaxRequest16 = null;
AjaxRequest16 = getXmlHttpRequestObject(); // method here to load the request
if(AjaxRequest16.readyState == 4 || AjaxRequest16.readyState == 0)
{
AjaxRequest16.open("GET", "ajax.php?id=16&AreaID=" +encodeURIComponent(AreaID)+ "&month="
+encodeURIComponent(document.getElementById("cboMonths").value)+ "&TimeSlot=" +encodeURIComponent(x), true);
AjaxRequest16.send(null);
AjaxRequest16.onreadystatechange = function()
{
if(AjaxRequest16.readyState == 4)
{
var innerHTML = AjaxRequest16.responseText.toString();
/* Retrieve data from the server and display. */
document.getElementById("divTime"+x).innerHTML = innerHTML;
}/* end if */
}/* end function */
}/* end if */
}
I changed all the code, but it does exactly what you want, without using asynchronous = false, and browser freezing:
function ajaxRequest(url, callback) {
var req = null;
if (window.XMLHttpRequest) req = new XMLHttpRequest();
else if (window.ActiveXObject) // if IE
{
try {
req = new ActiveXObject("Msxml2.XMLHTTP")
} catch (e) {
try {
req = new ActiveXObject("Microsoft.XMLHTTP")
} catch (e) {}
}
} else {
throw ("No Ajax support!");
return;
}
req.open('GET', url, true);
req.onreadystatechange = function () {
if (req.readyState == 4) {
if (typeof (callback) == "function") callback(req);
}
};
req.send(null);
return req;
}
function loadMyData() {
var x = parseInt(arguments[0]);
if (x > 23) {
alert("all 24 is loaded!");
}
var url = "ajax.php?id=16&AreaID=" + encodeURIComponent(AreaID) +
"&month=" + encodeURIComponent(document.getElementById("cboMonths").value) +
"&TimeSlot=" + encodeURIComponent(x);
var callback = Function('req', 'document.getElementById("divTime' + x + '").innerHTML =' +
' req.responseText;' +
'loadMyData(' + x + ');');
ajaxRequest(url, callback);
}
loadMyData(0);
you should make your ajax calls asenkron false try this:
for(var x=0 ; x<=23 ; x++)
{
AjaxRequest16 = null;
AjaxRequest16 = getXmlHttpRequestObject(); // method here to load the request
if(AjaxRequest16.readyState == 4 || AjaxRequest16.readyState == 0)
{
AjaxRequest16.open("GET", "ajax.php?id=16&AreaID=" +encodeURIComponent(AreaID)+ "&month="
+encodeURIComponent(document.getElementById("cboMonths").value)+ "&TimeSlot=" +encodeURIComponent(x), false);
AjaxRequest16.send(null);
AjaxRequest16.onreadystatechange = function()
{
if(AjaxRequest16.readyState == 4)
{
var innerHTML = AjaxRequest16.responseText.toString();
/* Retrieve data from the server and display. */
document.getElementById("divTime"+x).innerHTML = innerHTML;
}/* end if */
}/* end function */
}/* end if */
}/* end if */
Sequentially load content with ajax
here is a simple ajax function for modern browsers (chrome,safari,ie10,android,ios)
function ajax(a,b,c){//url,function,just a placeholder
c=new XMLHttpRequest;
c.open('GET',a);
c.onload=b;
c.send()
}
and this is how you load content sequentially
var current=0,
x=23;
function handler(){
document.getElementById("divTime"+current).innerHTML=this.response;
current++
if(current<x){
ajax('url.php?id='+current,handler)
}
}
ajax('url.php?id='+current,handler);
this way you don't overwrite previous ajax calls.
Multiple simultaneous ajax calls is a bad solution.
anyway to achieve multiple ajax calls at the same time you need to create multiple ajax request functions.
var ajaxcall=[];
ajaxcall[0]=new XMLHttpRequest;
ajaxcall[0].CUSTOMID=0;
ajaxcall[0].open('GET','url.php?id='+0);
ajaxcall[0].onload=function(){console.log(this.CUSTOMID,this.response)};
ajaxcall[0].send();
What it boils down to is the asynchronous nature of Ajax calls.
Each Ajax context must be kept alive until the request is over (completion or failure).
In your initial code, you use only one Ajax request context. The loop launches the first request, but then overwrites its context immediately with the second one long before the first was processed. When the server responds (a few milliseconds later), there is no handler left on the browser side to process the response (except for the 24th one).
What your workaround does is to create a different context and callback for each request, since your global function stores them in different closures.
However, as a result you will fire a hail of 24 Ajax requests simultaneously on the server, which is likely to cause unnecessary overhead, or even crashes if your PHP script does not expect to execute concurrently on the same request. Besides, synchronizing your code on completion of these requests will not be easy.
Here is what I use for my own apps :
// --------------------------------------------------------------------
// Ajax lite
// --------------------------------------------------------------------
function PageCache (target, void_contents)
{
this.text = {};
this.req = {};
this.void_contents = void_contents || 'void';
this.target = target;
}
PageCache.prototype = {
// synchronous load
load: function (page)
{
if (!this.text[page]) this.text[page] = this._launch_request (page);
return this.text[page];
},
// asynchronous load
fetch: function (page, action)
{
if (this.text[page])
{
action (this, page);
return;
}
if (this.req[page]) return;
this.req[page] = this._launch_request (page,
function(_this, _page, _action) {
return function(){
_this._loader(_page,_action);
};
}(this,page,action));
},
_launch_request: function (page, callback)
{
var req;
try {
req = window.XMLHttpRequest
? new XMLHttpRequest()
: new ActiveXObject("Microsoft.XMLHTTP");
}
catch (e) {}
req.open('GET', this.target.replace (/\$/, page), callback!=undefined);
if (callback) req.onreadystatechange = callback;
req.send(null);
return callback ? req : this._get_result (req);
},
_get_result: function (req)
{
return (req.status < 400)
? req.responseText
: this.void_contents;
},
_loader: function (page, action)
{
if (!this.req[page] || (this.req[page].readyState != 4)) return;
this.text[page] = this._get_result (this.req[page])
delete this.req[page];
if (action) action (this.text[page], page);
}
}
In your example, you could use it like so:
First, a bit of cleanup :
function compute_id (AreaID,x) {
return "id=16&AreaID="
+ encodeURIComponent(AreaID)
+ "&month="
+ encodeURIComponent(document.getElementById("cboMonths").value)
+ "&TimeSlot="
+ x; // I suspect all the encodeURIComponent() calls are unnecessary
}
var Ajax = new PageCache (
'ajax.php?$', // '$' will be replaced with fetch() parameter
'error loading data'); // contents in case of request failure
1) simultaneous requests (not recommended)
for (var x = 0; x != 24 ; x++) {
// store current x value in a closure
var request_done = function (_x) {
return function (responseText) {
document.getElementById("divTime"+_x).innerHTML = responseText;
}}(x);
}
Ajax.fetch (compute_id (AreaID,x), request_done);
}
2) sequential blocking requests (very bad, don't do it unless your code cannot proceed without the data)
for (var x = 0; x != 24 ; x++) {
document.getElementById("divTime"+x).innerHTML =
Ajax.load (compute_id (AreaID,x));
}
3) sequential non-blocking requests
var AjaxCtx = { x:0, max:24};
// launch first request
Ajax.fetch (compute_id (AreaID, AjaxCtx.x), request_done);
function request_done (responseText) {
document.getElementById("divTime"+AjaxCtx.x).innerHTML = responseText;
// request completion triggers the next
if (++AjaxCtx.x != AjaxCtx.max)
Ajax.fetch (compute_id (AreaID,AjaxCtx.x), request_done);
}