196

I have written a javascript function that uses setInterval to manipulate a string every tenth of a second for a certain number of iterations.

function timer() {
    var section = document.getElementById('txt').value;
    var len = section.length;
    var rands = new Array();

    for (i=0; i<len; i++) {
        rands.push(Math.floor(Math.random()*len));
    };

    var counter = 0
    var interval = setInterval(function() {
        var letters = section.split('');
        for (j=0; j < len; j++) {
            if (counter < rands[j]) {
                letters[j] = Math.floor(Math.random()*9);
            };
        };
        document.getElementById('txt').value = letters.join('');
        counter++

        if (counter > rands.max()) {
            clearInterval(interval);
        }
    }, 100);
};

Instead of having the interval set at a specific number, I would like to update it every time it runs, based on a counter. So instead of:

var interval = setInterval(function() { ... }, 100);

It would be something like:

var interval = setInterval(function() { ... }, 10*counter);

Unfortunately, that did not work. It seemed like "10*counter" equals 0.

So, how can I adjust the interval every time the anonymous function runs?

Sébastien Gicquel
  • 4,227
  • 7
  • 54
  • 84
Joe Di Stefano
  • 2,157
  • 2
  • 13
  • 8

17 Answers17

156

You could use an anonymous function:

var counter = 10;
var myFunction = function(){
    clearInterval(interval);
    counter *= 10;
    interval = setInterval(myFunction, counter);
}
var interval = setInterval(myFunction, counter);

UPDATE: As suggested by A. Wolff, use setTimeout to avoid the need for clearInterval.

var counter = 10;
var myFunction = function() {
    counter *= 10;
    setTimeout(myFunction, counter);
}
setTimeout(myFunction, counter);
nick
  • 3,544
  • 1
  • 26
  • 22
  • 5
    Well RozzA, my answer was posted on Sep 16 '11 and user28958's on Aug 22 '13, so I'll take the "rep" thanks! – nick Jan 28 '14 at 18:54
  • 14
    Why are you using an interval, a simple timeout would be better without the need to clear it. e.g: http://jsfiddle.net/fgs5nwgn/ – A. Wolff Dec 11 '14 at 21:14
  • 4
    I was sticking to the context of the question. setTimeout will work of course – nick Dec 11 '14 at 23:32
  • @A.Wolff Just a note, you don't need to define the timeout variables... They aren't doing anything – NerdOfCode Jun 14 '21 at 02:41
  • And how can stop loop the second code? – Nabi K.A.Z. Mar 03 '22 at 23:46
  • 1
    @NabiK.A.Z. You just need to wrap the setTimeout (inside the function) in an if statement. Here's a detailed example with explanations of how it might be used, https://jsfiddle.net/q4c2rk1a/ – Zei Jan 19 '23 at 02:06
130

Use setTimeout() instead. The callback would then be responsible for firing the next timeout, at which point you can increase or otherwise manipulate the timing.

EDIT

Here's a generic function you can use to apply a "decelerating" timeout for ANY function call.

function setDeceleratingTimeout(callback, factor, times)
{
    var internalCallback = function(tick, counter) {
        return function() {
            if (--tick >= 0) {
                window.setTimeout(internalCallback, ++counter * factor);
                callback();
            }
        }
    }(times, 0);

    window.setTimeout(internalCallback, factor);
};

// console.log() requires firebug    
setDeceleratingTimeout(function(){ console.log('hi'); }, 10, 10);
setDeceleratingTimeout(function(){ console.log('bye'); }, 100, 10);
Peter Bailey
  • 105,256
  • 31
  • 182
  • 206
  • 1
    By callback, do you mean the last line of the function calls itself recursively with a setTimeout(..., newInterval) ? – Marc Aug 14 '09 at 21:23
  • 1
    I assume that is what he meant. I just tried that and it seems to be working. Thanks, guys! – Joe Di Stefano Aug 14 '09 at 21:32
  • @joeydi, agreed. I just wanted to state it in case others dont! ;) – Marc Aug 14 '09 at 21:36
  • Added an example of a re-usable callable – Peter Bailey Aug 14 '09 at 21:39
  • 2
    only gives 9 hi's :) `--t` should probably be `t--` http://jsfiddle.net/albertjan/by5fd/ – albertjan Jul 13 '12 at 12:34
  • "exponential backoff" might be the key term here? Or I guess a little tweak might flip it over to achieve that functionality.. – obimod Dec 19 '13 at 23:05
  • 2
    Doing this recursively don't you run the risk of getting a stack overflow error if `times` is too large? – André C. Andersen Jan 12 '14 at 18:54
  • https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Increment – CrandellWS Feb 16 '16 at 01:40
  • 5
    Being nitpicky here, but I gotta say, that code is pretty hard to read. If you're gonna use next-line braces, at least have the decency to either use 4-8-space indentation or never go beyond 2 indents. IMO [this version](http://pastebin.com/Y2yiG4FW) is much easier to read. Also take note of the renaming of `t` to `tick`, which was my best guess as to whatever "t" is supposed to stand for. `t` is a pretty bad variable name. – Braden Best Jul 17 '16 at 01:47
  • Also, if you're wondering what I'm doing here, I found this question while looking up how to synchronize a function call with the system clock. JS doesn't seem to support that, so I ended up [rolling my own](http://pastebin.com/fwcwWKwr) – Braden Best Jul 18 '16 at 20:57
  • now I know why I was confused.. `internalCallback` is a self calling function. that (times, 0) had me confused because there were no () just before it, instead {}. – Nick Res Oct 29 '16 at 04:29
  • 1
    @AndréChristofferAndersen that's my concern as well. they're just deepening the stack with each call. i'm doing an interval of around 500ms for multi hour long periods. The stack will get enormous! – JacksonHaenchen Feb 03 '17 at 05:17
24

I like this question - inspired a little timer object in me:

window.setVariableInterval = function(callbackFunc, timing) {
  var variableInterval = {
    interval: timing,
    callback: callbackFunc,
    stopped: false,
    runLoop: function() {
      if (variableInterval.stopped) return;
      var result = variableInterval.callback.call(variableInterval);
      if (typeof result == 'number')
      {
        if (result === 0) return;
        variableInterval.interval = result;
      }
      variableInterval.loop();
    },
    stop: function() {
      this.stopped = true;
      window.clearTimeout(this.timeout);
    },
    start: function() {
      this.stopped = false;
      return this.loop();
    },
    loop: function() {
      this.timeout = window.setTimeout(this.runLoop, this.interval);
      return this;
    }
  };

  return variableInterval.start();
};

Example use

var vi = setVariableInterval(function() {
  // this is the variableInterval - so we can change/get the interval here:
  var interval = this.interval;

  // print it for the hell of it
  console.log(interval);

  // we can stop ourselves.
  if (interval>4000) this.stop();

  // we could return a new interval after doing something
  return interval + 100;
}, 100);  

// we can change the interval down here too
setTimeout(function() {
  vi.interval = 3500;
}, 1000);

// or tell it to start back up in a minute
setTimeout(function() {
  vi.interval = 100;
  vi.start();
}, 60000);
gnarf
  • 105,192
  • 25
  • 127
  • 161
23

I had the same question as the original poster, did this as a solution. Not sure how efficient this is ....

let interval = 5000; // initial condition
let run = setInterval(request, interval); // start setInterval as "run"

function request() {

    console.log(interval); // firebug or chrome log
    clearInterval(run); // stop the setInterval()

    // dynamically change the run interval
    if (interval > 200) {
        interval = interval * .8;
    } else {
        interval = interval * 1.2;
    }

    run = setInterval(request, interval); // start the setInterval()
}
Valor_
  • 3,461
  • 9
  • 60
  • 109
user28958
  • 291
  • 3
  • 7
  • i like this answer better because it *actually* answers OP (and my) question. setTimeout is subject to being delayed (by 100% cpu use, other scripts, etc) where as setInterval IS NOT affected by those delays--making it far superior for 'realtime' stuff – RozzA Dec 26 '13 at 20:59
  • 10
    I'm 99% sure that your claim about `setInterval` is wrong @RozzA - It is still subject to the same delays as any other JavaScript, and almost every browser also clamps setInterval to 4ms. Do you have a link to a post about this or something? – gnarf Apr 08 '14 at 12:19
16

This is my way of doing this, i use setTimeout:

var timer = {
    running: false,
    iv: 5000,
    timeout: false,
    cb : function(){},
    start : function(cb,iv){
        var elm = this;
        clearInterval(this.timeout);
        this.running = true;
        if(cb) this.cb = cb;
        if(iv) this.iv = iv;
        this.timeout = setTimeout(function(){elm.execute(elm)}, this.iv);
    },
    execute : function(e){
        if(!e.running) return false;
        e.cb();
        e.start();
    },
    stop : function(){
        this.running = false;
    },
    set_interval : function(iv){
        clearInterval(this.timeout);
        this.start(false, iv);
    }
};

Usage:

timer.start(function(){
    console.debug('go');
}, 2000);

timer.set_interval(500);

timer.stop();
Jaapze
  • 732
  • 6
  • 15
  • 1
    +1, I modified it slightly for my purposes so I can use multiple variable intervals - http://jsfiddle.net/h70mzvdq/ – Smern Sep 25 '16 at 17:04
  • Also modified the `set_interval` function to NOT kick off a new execution unless the new interval is smaller than the old one.. `if (iv < this.iv) { clearInterval(this.timeout); this.start(false, iv); } else { this.iv = iv; }` – Smern Sep 25 '16 at 17:56
  • I also liked this solution, but preferred if the timer did not get changed unless it was a 1/2 second different from the last. I modified ```set_interval``` function to be: ```let round = Math.trunc( iv / 500) * 500; if (round != this.iv ) { clearInterval( this.timeout ); this.start( false, round ); }``` – ztalbot May 19 '21 at 15:04
  • I modified this for my use-case too. – doptimusprime Jun 05 '21 at 07:41
12

A much simpler way would be to have an if statement in the refreshed function and a control to execute your command at regular time intervals . In the following example, I run an alert every 2 seconds and the interval (intrv) can be changed dynamically...

var i=1;
var intrv=2; // << control this variable

var refreshId = setInterval(function() {
  if(!(i%intrv)) {
    alert('run!');
  }
  i++;
}, 1000);
dmonaldo
  • 189
  • 2
  • 13
Kiril Cvetkov
  • 129
  • 1
  • 2
  • This is my personal favorite too. small, simple, extensible. – guy mograbi Jan 02 '17 at 23:30
  • I wanted a solution of a decelerating timer that could have its rate reset based on application events; this met that need simply and perfectly. Thank you. – Andrew Brown Mar 28 '17 at 18:22
  • It's cool but it also fires intervals at moments that you don't need it... also it's a bit unreadable. For these reasons I'd prefer setTimeout personally. – Kokodoko May 26 '17 at 16:34
4

This can be initiated however you want. timeout is the method i used to keep it on the top of the hour.

I had the need for every hour to begin a code block on the hour. So this would start at server startup and run the interval hourly. Basicaly the initial run is to begin the interval within the same minute. So in a second from init, run immediately then on every 5 seconds.

var interval = 1000;
var timing =function(){
    var timer = setInterval(function(){
        console.log(interval);
        if(interval == 1000){ /*interval you dont want anymore or increment/decrement */
            interval = 3600000; /* Increment you do want for timer */
            clearInterval(timer);
            timing();
        }
    },interval);
}
timing();

Alternately if you wanted to just have something happen at start and then forever at a specific interval you could just call it at the same time as the setInterval. For example:

var this = function(){
 //do
}
setInterval(function(){
  this()
},3600000)
this()

Here we have this run the first time and then every hour.

Dgerena
  • 41
  • 2
2

I couldn't synchronize and change the speed my setIntervals too and I was about to post a question. But I think I've found a way. It should certainly be improved because I'm a beginner. So, I'd gladly read your comments/remarks about this.

<body onload="foo()">
<div id="count1">0</div>
<div id="count2">2nd counter is stopped</div>
<button onclick="speed0()">pause</button>
<button onclick="speedx(1)">normal speed</button>
<button onclick="speedx(2)">speed x2</button>
<button onclick="speedx(4)">speed x4</button>
<button onclick="startTimer2()">Start second timer</button>
</body>
<script>
var count1 = 0,
    count2 = 0,
    greenlight = new Boolean(0), //blocks 2nd counter
    speed = 1000,   //1second
    countingSpeed;
function foo(){
    countingSpeed = setInterval(function(){
        counter1();
        counter2();
    },speed);
}
function counter1(){
    count1++;
    document.getElementById("count1").innerHTML=count1;
}
function counter2(){
    if (greenlight != false) {
        count2++;
        document.getElementById("count2").innerHTML=count2;
    }
}
function startTimer2(){
    //while the button hasn't been clicked, greenlight boolean is false
    //thus, the 2nd timer is blocked
    greenlight = true;
    counter2();
    //counter2() is greenlighted
}

//these functions modify the speed of the counters
function speed0(){
    clearInterval(countingSpeed);
}
function speedx(a){
    clearInterval(countingSpeed);
    speed=1000/a;
    foo();
}
</script>

If you want the counters to begin to increase once the page is loaded, put counter1() and counter2() in foo() before countingSpeed is called. Otherwise, it takes speed milliseconds before execution. EDIT : Shorter answer.

2
(function variableInterval() {
    //whatever needs to be done
    interval *= 2; //deal with your interval
    setTimeout(variableInterval, interval);
    //whatever needs to be done
})();

can't get any shorter

mikakun
  • 2,203
  • 2
  • 16
  • 24
2

Here is yet another way to create a decelerating/accelerating interval timer. The interval gets multiplied by a factor until a total time is exceeded.

function setChangingInterval(callback, startInterval, factor, totalTime) {
    let remainingTime = totalTime;
    let interval = startInterval;

    const internalTimer = () => {
        remainingTime -= interval ;
        interval *= factor;
        if (remainingTime >= 0) {
            setTimeout(internalTimer, interval);
            callback();
        }
    };
    internalTimer();
}
jo_va
  • 13,504
  • 3
  • 23
  • 47
2

You can do this by clearing the interval every iteration, changing the timer value and setting the interval again. Hope it helps ;)

For exemple:

const DOMCounter = document.querySelector(".counter")

let timer = 1000

const changeCounter = () => {
  clearInterval(interval)
  DOMCounter.innerHTML = timer
  timer += 1000
  timer == 5000 && timer == 1000
  interval = setInterval(changeCounter, timer)
}

let interval = setInterval(changeCounter, timer)
<div class="container">
  <p class="counter"></p>
</div>
1

This piece of code below accelerates (acceleration > 1) or decelerates (acceleration <1) a setInterval function :

function accelerate(yourfunction, timer, refresh, acceleration) {
    var new_timer = timer / acceleration;
    var refresh_init = refresh;//save this user defined value
    if (refresh < new_timer ){//avoid reseting the interval before it has produced anything.
        refresh = new_timer + 1 ;
    };
    var lastInter = setInterval(yourfunction, new_timer);
    console.log("timer:", new_timer);
    function stopLastInter() {
        clearInterval(lastInter);
        accelerate(yourfunction, new_timer, refresh_init, acceleration);
        console.log("refresh:", refresh);
    };
    setTimeout(stopLastInter, refresh);
}

With :

  • timer: the setInterval initial value in ms (increasing or decreasing)
  • refresh: the time before a new value of timer is calculated. This is the step lenght
  • acceleration: the gap between the old and the next timer value. This is the step height
mquantin
  • 1,085
  • 8
  • 23
  • i'm blind or there is no `factor` in your code? – Alynva Nov 03 '21 at 07:35
  • also, how to stop it? – Alynva Nov 03 '21 at 07:42
  • Thx. I corrected the answer: `factor` was the old name for `acceleration`...it's clearer now! sorry for that. About "how to stop it": i would pass a var (`continue = true`) to the accelerate function, and add a first line in the accelerate function : `while (continue) {` – mquantin Nov 04 '21 at 13:28
  • I suggest that to stop, you return a function that clear the interval and the timeout, and when it calls the `accelerate` again, you store the function that it returns to call it too – Alynva Nov 05 '21 at 15:44
  • I created a pen here with my changes: https://codepen.io/Alynva/pen/vYJdwQY?editors=0011 – Alynva Nov 05 '21 at 15:59
1

Make new function:

// set Time interval
$("3000,18000").Multitimeout();

jQuery.fn.extend({
    Multitimeout: function () {
        var res = this.selector.split(",");
        $.each(res, function (index, val) { setTimeout(function () { 
            //...Call function
            temp();
        }, val); });
        return true;
    }
});

function temp()
{
    alert();
}
Eric Aya
  • 69,473
  • 35
  • 181
  • 253
doshi smit
  • 29
  • 1
  • 7
0

Inspired by the internal callback above, i made a function to fire a callback at fractions of minutes. If timeout is set to intervals like 6 000, 15 000, 30 000, 60 000 it will continuously adapt the intervals in sync to the exact transition to the next minute of your system clock.

//Interval timer to trigger on even minute intervals
function setIntervalSynced(callback, intervalMs) {

    //Calculate time to next modulus timer event
    var betterInterval = function () {
        var d = new Date();
        var millis = (d.getMinutes() * 60 + d.getSeconds()) * 1000 + d.getMilliseconds();
        return intervalMs - millis % intervalMs;
    };

    //Internal callback
    var internalCallback = function () {
        return function () {
            setTimeout(internalCallback, betterInterval());
            callback();
        }
    }();

    //Initial call to start internal callback
    setTimeout(internalCallback, betterInterval());
};
flodis
  • 1,123
  • 13
  • 9
0

You can use a variable and change the variable instead.

setInterval(() => function, variable)
dsadnick
  • 624
  • 1
  • 7
  • 25
0

This is my idea for times when you do not want loops like setInterval to overlap.
You also want to be able to set the loop execution delay and start and stop the loop, instansly on the fly.
I am using a loop_flag variable and a setTimeout function.
I set the main function to async so that you can call other functions in the body by calling await. When the main body of your code is running, the main loop waits and does not repeat itself. (which is not the case with setInterval)

An example of a simple code is:

//@NabiKAZ

document.getElementById("btn_start").addEventListener("click", function() {
  console.log("Starting...");
  loop_flag = true;
  loop_func();
});
document.getElementById("btn_stop").addEventListener("click", function() {
  console.log("Stoping...");
  loop_flag = false;
});

var n = 0;

var loop_flag = false;
var loop_func = async function() {
  if (!loop_flag) {
    console.log("STOP.");
    return;
  }

  //body main function inhere
  n++;
  console.log(n);
  ////


  if (loop_flag) {
    setTimeout(loop_func, document.getElementById("inp_delay").value);
  } else {
    console.log("STOP.");
  }
}
<input id="inp_delay" value="1000">
<button id="btn_start">START</button>
<button id="btn_stop">STOP</button>

For a more complete code with a fetch request inside the loop, see here:

https://jsfiddle.net/NabiKAZ/a5hdw2bo/

Nabi K.A.Z.
  • 9,887
  • 6
  • 59
  • 81
-1
var counter = 15;
var interval = function() {
    setTimeout(function(){
        // Write your code here and remove console.log, remember that you need declare yourDynamicValue and give it a value
        console.log((new Date()).getTime())

        window.counter = yourDynamicValue;
        window.interval();
    }, counter);
}

// It needs to run just once as init

interval();

Hamidreza
  • 1,825
  • 4
  • 29
  • 40
  • 1
    give me error: `Uncaught TypeError: interval is not a function` but this worked: http://jsfiddle.net/fgs5nwgn/ – Nabi K.A.Z. Jul 01 '17 at 07:51