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?

Joe Di Stefano
la source

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);
Peter Bailey
la source
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);
pseudo
la source
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);
gnarf
la source
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()

    }
user28958
la source
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();
Jaapze
la source
+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);
Kiril Cvetkov
la source
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.

Dgerena
la source
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 .

vbarbarosh
la source
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.

BEAGMB
la source
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

Mikakun
la source
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
mquantin
la source
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();
}
doshi smit
la source
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();
}
jo_va
la source
0
var counter = 15;
var interval = setTimeout(function(){
    // your interval code here
    window.counter = dynamicValue;
    interval();
}, counter);
Hamidreza
la source
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());
};
flodis
la source