Modification de l'intervalle de SetInterval pendant son exécution


161

J'ai écrit une fonction javascript qui utilise setInterval pour manipuler une chaîne tous les dixièmes de seconde pendant un certain nombre d'itérations.

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

Au lieu d'avoir l'intervalle fixé à un nombre spécifique, je voudrais le mettre à jour à chaque fois qu'il s'exécute, en fonction d'un compteur. Donc au lieu de:

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

Ce serait quelque chose comme:

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

Malheureusement, cela n'a pas fonctionné. Il semblait que "10 * compteur" égale 0.

Alors, comment puis-je ajuster l'intervalle chaque fois que la fonction anonyme s'exécute?

Réponses:


107

Utilisez setTimeout()plutôt. Le rappel serait alors responsable du déclenchement du prochain délai d'expiration, auquel point vous pouvez augmenter ou manipuler le timing.

ÉDITER

Voici une fonction générique que vous pouvez utiliser pour appliquer un délai de "décélération" pour TOUT appel de fonction.

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

1
Par callback, voulez-vous dire que la dernière ligne de la fonction s'appelle elle-même récursivement avec un setTimeout (..., newInterval)?
Marc

1
Je suppose que c'est ce qu'il voulait dire. Je viens d'essayer et cela semble fonctionner. Merci les gars!
Joe Di Stefano

2
ne donne que 9 salutations :) --tdevrait probablement être t-- jsfiddle.net/albertjan/by5fd
albertjan

1
En faisant cela de manière récursive, ne courez-vous pas le risque d'obtenir une erreur de débordement de pile si elle timesest trop grande?
André C. Andersen

4
Être pointilleux ici, mais je dois dire que ce code est assez difficile à lire. Si vous allez utiliser des accolades de ligne suivante, ayez au moins la décence d'utiliser une indentation de 4 à 8 espaces ou de ne jamais aller au-delà de 2 indentations. IMO cette version est beaucoup plus facile à lire. Prenez également note du changement de nom de tto tick, qui était ma meilleure estimation quant à ce que "t" est censé représenter. test un nom de variable assez mauvais.
Braden Best

124

Vous pouvez utiliser une fonction anonyme:

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

MISE À JOUR: Comme suggéré par A. Wolff, utilisez setTimeoutpour éviter le besoin de clearInterval.

var counter = 10;
var myFunction = function() {
    counter *= 10;
    setTimeout(myFunction, counter);
}
setTimeout(myFunction, counter);

5
Eh bien RozzA, ma réponse a été publiée le 16 septembre 2011 et celle de user28958 le 22 août 2013, donc je vais prendre le "représentant" merci!
pseudo le

12
Pourquoi utilisez-vous un intervalle, un simple délai d'attente serait préférable sans qu'il soit nécessaire de l'effacer. par exemple: jsfiddle.net/fgs5nwgn
A. Wolff

4
Je m'en tenais au contexte de la question. setTimeout fonctionnera bien sûr
nick

24

J'aime cette question - a inspiré un petit objet de minuterie en moi:

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

Exemple d'utilisation

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

4
Merci - mettez-moi dans la bonne direction pour quelque chose de similaire sur lequel je travaille.
Colonel Sponsz

Simple et efficace. Merci!
Jester

18

J'avais la même question que l'affiche originale, je l'ai fait comme solution. Je ne sais pas à quel point c'est efficace ....

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

    }

J'aime mieux cette réponse car elle répond en fait à OP (et à ma) question. setTimeout est sujet à être retardé (par une utilisation à 100% du processeur, d'autres scripts, etc.) alors que setInterval N'EST PAS affecté par ces retards - ce qui le rend bien supérieur pour les trucs en `` temps réel ''
RozzA

8
Je suis sûr à 99% que votre affirmation setIntervalest fausse @RozzA - Il est toujours soumis aux mêmes retards que tout autre JavaScript, et presque tous les navigateurs fixent également setInterval à 4ms. Avez-vous un lien vers un article sur ceci ou quelque chose?
gnarf

9

C'est ma façon de faire, j'utilise 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();

+1, je l'ai légèrement modifié pour mes besoins afin que je puisse utiliser plusieurs intervalles variables - jsfiddle.net/h70mzvdq
Dallas

Également modifié la set_intervalfonction pour ne PAS lancer une nouvelle exécution à moins que le nouvel intervalle ne soit plus petit que l'ancien.if (iv < this.iv) { clearInterval(this.timeout); this.start(false, iv); } else { this.iv = iv; }
Dallas

9

Un moyen beaucoup plus simple serait d'avoir une ifinstruction dans la fonction rafraîchie et un contrôle pour exécuter votre commande à intervalles de temps réguliers. Dans l'exemple suivant, j'exécute une alerte toutes les 2 secondes et l'intervalle ( intrv) peut être modifié dynamiquement ...

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

var refreshId = setInterval(function() {
  if(!(i%intrv)) {
    alert('run!');
  }
  i++;
}, 1000);

C'est aussi mon préféré. petit, simple, extensible.
guy mograbi

Je voulais une solution d'une minuterie de décélération qui pourrait avoir son taux réinitialisé en fonction des événements de l'application; cela répondait simplement et parfaitement à ce besoin. Je vous remercie.
Andrew Brown

C'est cool mais ça déclenche aussi des intervalles à des moments où vous n'en avez pas besoin ... c'est aussi un peu illisible. Pour ces raisons, je préfère personnellement setTimeout.
Kokodoko

4

Cela peut être initié comme vous le souhaitez. timeout est la méthode que j'ai utilisée pour le garder en haut de l'heure.

J'avais besoin de chaque heure pour commencer un bloc de code sur l'heure. Donc, cela commencerait au démarrage du serveur et exécuterait l'intervalle toutes les heures. Fondamentalement, l'analyse initiale consiste à commencer l'intervalle dans la même minute. Donc, dans une seconde à partir de init, exécutez immédiatement puis toutes les 5 secondes.

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

Alternativement, si vous vouliez simplement que quelque chose se produise au début, puis pour toujours à un intervalle spécifique, vous pouvez simplement l'appeler en même temps que setInterval. Par exemple:

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

Ici, nous avons cette exécution la première fois, puis toutes les heures.


3

La réponse simple est que vous ne pouvez pas mettre à jour un intervalle de minuterie déjà créé . (Il n'y a que deux fonctions setInterval/setTimeret clearInterval/clearTimer, donc, avoir un, timerIdvous ne pouvez que le désactiver.) Mais vous pouvez faire quelques solutions de contournement. Jetez un œil à ce dépôt github .


2

Je ne pouvais pas synchroniser et modifier la vitesse de mes setIntervals aussi et j'étais sur le point de poster une question. Mais je pense avoir trouvé un moyen. Cela devrait certainement être amélioré car je suis un débutant. Je serais donc ravi de lire vos commentaires / remarques à ce sujet.

<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 vous voulez que les compteurs pour commencer à augmenter une fois que la page est chargée, mis counter1()et counter2()en foo()avant countingSpeedest appelé. Sinon, il faut quelques speedmillisecondes avant l'exécution. EDIT: Réponse plus courte.


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

ne peut pas être plus court


1

Je suis un débutant en javascript, et je n'ai trouvé aucune aide dans les réponses précédentes (mais beaucoup de bonnes idées).
Ce morceau de code ci-dessous accélère (accélération> 1) ou décélère (accélération <1). J'espère que cela pourrait aider certaines personnes:

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

Avec :

  • timer: la valeur initiale de setInterval en ms (croissante ou décroissante)
  • refresh: le temps avant qu'une nouvelle valeur de timersoit calculée. C'est la longueur du pas
  • factor: l'écart entre l'ancienne et la prochaine timervaleur. C'est la hauteur de marche

1

Faire une nouvelle fonction:

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

1

Voici encore une autre façon de créer une minuterie d'intervalle de décélération / accélération. L'intervalle est multiplié par un facteur jusqu'à ce qu'un temps total soit dépassé.

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

0
var counter = 15;
var interval = setTimeout(function(){
    // your interval code here
    window.counter = dynamicValue;
    interval();
}, counter);

donnez-moi une erreur: Uncaught TypeError: interval is not a functionmais cela a fonctionné: jsfiddle.net/fgs5nwgn
Nabi KAZ

0

Inspiré par le rappel interne ci-dessus, j'ai créé une fonction pour déclencher un rappel à des fractions de minutes. Si le délai d'expiration est défini sur des intervalles tels que 6 000, 15 000, 30 000, 60 000, il adaptera continuellement les intervalles synchronisés à la transition exacte vers la minute suivante de votre horloge système.

//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());
};
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.