Cambiar el intervalo de SetInterval mientras se está ejecutando

161

He escrito una función de JavaScript que usa setInterval para manipular una cadena cada décima de segundo durante un cierto número de iteraciones.

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);
};

En lugar de tener el intervalo establecido en un número específico, me gustaría actualizarlo cada vez que se ejecuta, basado en un contador. Entonces en lugar de:

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

Sería algo así como:

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

Lamentablemente, eso no funcionó. Parecía que "10 * counter" es igual a 0.

Entonces, ¿cómo puedo ajustar el intervalo cada vez que se ejecuta la función anónima?

Joe Di Stefano
fuente

Respuestas:

107

Usar en su setTimeout()lugar. La devolución de llamada sería responsable de disparar el próximo tiempo de espera, momento en el que puede aumentar o manipular el tiempo.

EDITAR

Aquí hay una función genérica que puede usar para aplicar un tiempo de espera de "desaceleración" para CUALQUIER llamada de función.

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
fuente
1
Por devolución de llamada, ¿quiere decir que la última línea de la función se llama recursivamente con un setTimeout (..., newInterval)?
Marc
1
Supongo que eso es lo que quiso decir. Acabo de intentar eso y parece estar funcionando. ¡Gracias chicos!
Joe Di Stefano
2
solo da 9 hola :) --tprobablemente debería ser t-- jsfiddle.net/albertjan/by5fd
albertjan
1
Al hacer esto de forma recursiva, ¿no corre el riesgo de obtener un error de desbordamiento de pila si timeses demasiado grande?
André C. Andersen
44
Ser quisquilloso aquí, pero debo decir que ese código es bastante difícil de leer. Si va a usar llaves de la siguiente línea, al menos tenga la decencia de usar una sangría de 4-8 espacios o nunca ir más allá de 2 sangrías. OMI, esta versión es mucho más fácil de leer. También tome nota del cambio de nombre de tto tick, que fue mi mejor suposición en cuanto a lo que se supone que significa "t". tes un nombre de variable bastante malo.
Braden Best
124

Podría usar una función anónima:

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

ACTUALIZACIÓN: Según lo sugerido por A. Wolff, use setTimeoutpara evitar la necesidad de clearInterval.

var counter = 10;
var myFunction = function() {
    counter *= 10;
    setTimeout(myFunction, counter);
}
setTimeout(myFunction, counter);
mella
fuente
55
Bueno, RozzA, mi respuesta fue publicada el 16 de septiembre del 11 y la del usuario 28958 el 22 de agosto del 13, así que tomaré el "representante" ¡gracias!
nick
12
¿Por qué está utilizando un intervalo? Un tiempo de espera simple sería mejor sin la necesidad de borrarlo. por ejemplo: jsfiddle.net/fgs5nwgn
A. Wolff
44
Me estaba pegando al contexto de la pregunta. setTimeout funcionará, por supuesto
nick
24

Me gusta esta pregunta, inspiró un pequeño objeto temporizador en mí:

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();
};

Ejemplo de uso

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
fuente
44
Gracias, ponme en la dirección correcta para algo similar en lo que estoy trabajando.
Coronel Sponsz
Simple y efectivo. ¡Gracias!
Jester
18

Tenía la misma pregunta que el póster original, hice esto como una solución. No estoy seguro de cuán eficiente es esto ...

interval = 5000; // initial condition
var 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()

    }
usuario28958
fuente
Me gusta más esta respuesta porque en realidad responde la pregunta OP (y mi). setTimeout está sujeto a retrasos (por el uso del 100% de la CPU, otros scripts, etc.) mientras que setInterval NO se ve afectado por esos retrasos, lo que lo hace muy superior para las cosas en 'tiempo real'
RozzA
8
Estoy 99% seguro de que su reclamo setIntervales incorrecto @RozzA: todavía está sujeto a los mismos retrasos que cualquier otro JavaScript, y casi todos los navegadores también fijan setInterval a 4 ms. ¿Tienes un enlace a una publicación sobre esto o algo así?
Gnarf
9

Esta es mi forma de hacerlo, uso 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);
    }
};

Uso:

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

timer.set_interval(500);

timer.stop();
Jaapze
fuente
+1, lo modifiqué un poco para mis propósitos, así puedo usar múltiples intervalos variables - jsfiddle.net/h70mzvdq
Dallas
También modificó la set_intervalfunción para NO iniciar una nueva ejecución a menos que el nuevo intervalo sea más pequeño que el anterior ...if (iv < this.iv) { clearInterval(this.timeout); this.start(false, iv); } else { this.iv = iv; }
Dallas
9

Una forma mucho más simple sería tener una ifdeclaración en la función actualizada y un control para ejecutar su comando a intervalos de tiempo regulares. En el siguiente ejemplo, ejecuto una alerta cada 2 segundos y el intervalo ( intrv) se puede cambiar dinámicamente ...

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

var refreshId = setInterval(function() {
  if(!(i%intrv)) {
    alert('run!');
  }
  i++;
}, 1000);
Kiril Cvetkov
fuente
Este es mi favorito personal también. Pequeño, simple, extensible.
Guy Mograbi
Quería una solución de un temporizador de desaceleración que pudiera restablecer su velocidad en función de los eventos de la aplicación; Esto satisfizo esa necesidad simple y perfectamente. Gracias.
Andrew Brown
Es genial, pero también dispara intervalos en momentos en que no lo necesitas ... también es un poco ilegible. Por estas razones, preferiría setTimeout personalmente.
Kokodoko
4

Esto se puede iniciar como quieras. el tiempo de espera es el método que utilicé para mantenerlo en la cima de la hora.

Tenía la necesidad de cada hora para comenzar un bloque de código en la hora. Entonces, esto comenzaría al inicio del servidor y ejecutaría el intervalo cada hora. Básicamente, la ejecución inicial es comenzar el intervalo dentro del mismo minuto. Entonces, en un segundo desde init, ejecute inmediatamente cada 5 segundos.

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();

Alternativamente, si desea que algo suceda al inicio y luego para siempre en un intervalo específico, puede llamarlo al mismo tiempo que setInterval. Por ejemplo:

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

Aquí tenemos esta ejecución la primera vez y luego cada hora.

Dgerena
fuente
3

La respuesta simple es que no puede actualizar un intervalo de temporizador ya creado . (Solo hay dos funciones setInterval/setTimery clearInterval/clearTimer, por lo tanto, tener una timerIdsolo puede desactivarla). Pero puede hacer algunas soluciones. Echa un vistazo a este repositorio de Github .

vbarbarosh
fuente
2

No pude sincronizar y cambiar la velocidad de mis SetIntervals también y estaba a punto de publicar una pregunta. Pero creo que he encontrado el camino. Sin duda debería mejorarse porque soy un principiante. Por lo tanto, con mucho gusto leería sus comentarios / observaciones sobre esto.

<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>

Si desea que los contadores que comienzan a aumentar una vez que se carga la página, poner counter1()y counter2()en foo()antes de countingSpeedque se llama. De lo contrario, toma speedmilisegundos antes de la ejecución. EDITAR: respuesta más corta.

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

no puede acortarse

Mikakun
fuente
1

Soy un principiante en javascript y no encontré ninguna ayuda en las respuestas anteriores (pero muchas buenas ideas).
Este fragmento de código a continuación acelera (aceleración> 1) o desacelera (aceleración <1). Espero que pueda ayudar a algunas personas:

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);
}

Con :

  • timer: el valor inicial setInterval en ms (aumentando o disminuyendo)
  • refresh: el tiempo antes de que timerse calcule un nuevo valor de . Este es el paso largo
  • factor: la brecha entre el valor anterior y el siguiente timer. Esta es la altura del escalón
mquantin
fuente
1

Hacer nueva función:

// 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();
}
doshi smit
fuente
1

Aquí hay otra forma de crear un temporizador de intervalo de desaceleración / aceleración. El intervalo se multiplica por un factor hasta que se excede el tiempo total.

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
fuente
0
var counter = 15;
var interval = setTimeout(function(){
    // your interval code here
    window.counter = dynamicValue;
    interval();
}, counter);
Hamidreza
fuente
dame un error: Uncaught TypeError: interval is not a functionpero esto funcionó: jsfiddle.net/fgs5nwgn
Nabi KAZ
0

Inspirado por la devolución de llamada interna anterior, hice una función para disparar una devolución de llamada a fracciones de minutos. Si el tiempo de espera se establece en intervalos como 6 000, 15 000, 30 000, 60 000, adaptará continuamente los intervalos sincronizados a la transición exacta al siguiente minuto del reloj de su sistema.

//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
fuente