问题
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.
回答1:
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.
回答2:
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.
回答3:
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();
});
}
});
来源:https://stackoverflow.com/questions/45233377/settimeout-not-delaying-function-call-in-each