I've written a general purpose repeat function which allows you to repeat a callback function X times separated by I intervals with the option to start immediately or after the interval. It can also default to just looping infinitely, which is what setInterval
does. Is there a better approach, or any improvements people can provide? Make sure to comment out all but one of the examples :)
function repeatXI(callback, interval, repeat, immediate) {
repeat = typeof repeat == 'undefined' ? -1 : repeat;
interval = interval <= 0 ? 1000 : interval;
immediate = typeof immediate == 'undefined' ? false : immediate;
var offset = immediate ? 0 : 1;
var id = null;
if (repeat > 0) {
for (var i = 0; i < repeat; i++) {
id = setTimeout(callback, interval * (i + offset));
}
} else {
id = setInterval(callback, interval);
}
return id;
}
// Example
var someFunc = function () {console.log(1);}
// Repeats forever, 1 second apart
repeatXI(someFunc, 1000);
// Repeats 10 times, 1 second apart, beginning after 1 second
repeatXI(someFunc, 1000, 10);
// Same as above, but beginning now
repeatXI(someFunc, 1000, 10, true);
-
\$\begingroup\$ Also good to note the difference in setTimeout and setInterval - stackoverflow.com/questions/729921/settimeout-or-setinterval \$\endgroup\$Thomas– Thomas2015年04月08日 22:46:37 +00:00Commented Apr 8, 2015 at 22:46
2 Answers 2
I don't see anything problematic with your code, but just for fun, here are some alternative approaches to various parts of the code. It's 100% DRY, and functionally identical to the original.
function repeatXI(callback, interval, repeats, immediate) {
var timer, trigger;
trigger = function () {
callback();
--repeats || clearInterval(timer);
};
interval = interval <= 0 ? 1000 : interval; // default: 1000ms
repeats = parseInt(repeats, 10) || 0; // default: repeat forever
timer = setInterval(trigger, interval);
if( !!immediate ) { // Coerce boolean
trigger();
}
}
If you want, you can return timer
from the function (as I do in the demo), so you can clear it (i.e. cancel the repeater) elsewhere in your code.
Regardless of the implementation-specifics it's worth noting that timers are unreliable at the best of times (since JS is single-threaded). Depending on your needs it may not be a good idea to set n timeouts at n * interval
as you do for finite repeats. If a few of the timeouts get blocked long enough, they'll all queue up, and fire at once, once the thread clears. With setInterval
though, you're guaranteed that there'll be at least interval
time between the callbacks.
So it depends on your needs how you want to implement it.
-
\$\begingroup\$ good point regarding the intervals! \$\endgroup\$Aram Kocharyan– Aram Kocharyan2012年06月26日 01:52:15 +00:00Commented Jun 26, 2012 at 1:52
-
\$\begingroup\$ @AramKocharyan No prob. Don't forget to click the checkmark on mine or Joseph's answer - whichever one you think helped the most \$\endgroup\$Flambino– Flambino2012年06月27日 02:03:45 +00:00Commented Jun 27, 2012 at 2:03
It's pretty much commented in the code. The usage is still the same as the one you used. Here's a demo.
function repeatXI(callback, interval, repetitions, immediate) {
//general purpose repeater function
function repeater(repetitions) {
if (repetitions >= 0) {
//use call or apply so we can specify "this"
callback.call(this);
//repeat
setTimeout(function() {
//the idea of passing the repetition count replaces the loop
//the -- means that the initial iteration turns the repeat to -1
//since the condition runs when >=0 repetitions
repeater(--repetitions);
}, interval);
}
}
//set defaults using ||
//|| means "use this value OR the default"
//if you want to be strict, you can do type checks instead
repetitions = repetitions || 0;
interval = interval || 1000;
//if immediate, call instantly, else, delay
if (immediate) {
console.log('immediate');
repeater(--repetitions);
} else {
console.log('delayed');
setTimeout(function() {
repeater(--repetitions);
}, interval);
}
}
Witout comments and packed:
function repeatXI(callback, interval, repetitions, immediate) {
function repeater(repetitions) {
if (repetitions >= 0) {
callback.call(this);
setTimeout(function () {
repeater(--repetitions)
}, interval)
}
}
repetitions = repetitions || 0;
interval = interval || 1000;
if (immediate) {
repeater(--repetitions)
} else {
setTimeout(function () {
repeater(--repetitions)
}, interval)
}
}