Zmiana interwału SetInterval podczas jego działania

161

Napisałem funkcję javascript, która używa setInterval do manipulowania łańcuchem znaków co dziesiątą część sekundy przez określoną liczbę iteracji.

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

Zamiast ustawiać interwał na określoną liczbę, chciałbym aktualizować go za każdym razem, gdy jest uruchomiony, na podstawie licznika. Więc zamiast:

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

To byłoby coś takiego:

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

Niestety to nie zadziałało. Wyglądało na to, że „10 * licznik” równa się 0.

Jak więc mogę dostosować interwał przy każdym uruchomieniu funkcji anonimowej?

Joe Di Stefano
źródło

Odpowiedzi:

107

Użyj setTimeout()zamiast tego. Wywołanie zwrotne byłoby wtedy odpowiedzialne za uruchomienie następnego limitu czasu, w którym to momencie można zwiększyć lub w inny sposób manipulować taktowaniem.

EDYTOWAĆ

Oto ogólna funkcja, której można użyć do zastosowania „zwalniającego” limitu czasu dla DOWOLNEGO wywołania funkcji.

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
źródło
1
Czy przez wywołanie zwrotne masz na myśli, że ostatnia linia funkcji wywołuje się rekurencyjnie za pomocą setTimeout (..., newInterval)?
Marc
1
Zakładam, że to miał na myśli. Właśnie tego spróbowałem i wydaje się, że działa. Dzięki chłopaki!
Joe Di Stefano
2
daje tylko 9 cześć :) --tprawdopodobnie powinno być t-- jsfiddle.net/albertjan/by5fd
albertjan
1
Czy robiąc to rekurencyjnie, nie ryzykujesz wystąpienia błędu przepełnienia stosu, jeśli timesjest zbyt duży?
André C. Andersen
4
Jestem tu drapieżny, ale muszę powiedzieć, że ten kod jest dość trudny do odczytania. Jeśli zamierzasz używać nawiasów klamrowych w następnej linii, przynajmniej miej na tyle przyzwoitości, aby użyć wcięcia 4-8 spacji lub nigdy nie wychodzić poza dwa wcięcia. IMO ta wersja jest znacznie łatwiejsza do odczytania. Zwróć także uwagę na zmianę nazwy na tto tick, co było moim najlepszym przypuszczeniem, co ma oznaczać „t”. tto całkiem zła nazwa zmiennej.
Braden Best
124

Możesz użyć anonimowej funkcji:

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

AKTUALIZACJA: zgodnie z sugestią A. Wolffa, użyj, setTimeoutaby uniknąć potrzeby clearInterval.

var counter = 10;
var myFunction = function() {
    counter *= 10;
    setTimeout(myFunction, counter);
}
setTimeout(myFunction, counter);
nacięcie
źródło
5
Cóż, RozzA, moja odpowiedź została wysłana 16 września 2011 r., A user28958 22 sierpnia 2013 r., Więc wezmę „przedstawiciela” dzięki!
nick
12
Dlaczego używasz interwału? Prosty limit czasu byłby lepszy bez konieczności jego czyszczenia. np .: jsfiddle.net/fgs5nwgn
A. Wolff,
4
Trzymałem się kontekstu pytania. setTimeout oczywiście zadziała
nick
24

Podoba mi się to pytanie - zainspirowało mnie mały obiekt z zegarem:

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

Przykładowe zastosowanie

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
źródło
4
Dzięki - skieruj mnie w dobrym kierunku po coś podobnego, nad czym pracuję.
pułkownik Sponsz
Proste i skuteczne. Dzięki!
Jester
18

Miałem to samo pytanie co oryginalny plakat, zrobiłem to jako rozwiązanie. Nie jestem pewien, jak wydajne jest to ....

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
źródło
bardziej podoba mi się ta odpowiedź, ponieważ faktycznie odpowiada na pytanie OP (i moje). setTimeout może być opóźniony (o 100% użycie procesora, inne skrypty itp.), gdzie setInterval NIE JEST dotknięty tymi opóźnieniami - co czyni go znacznie lepszym w przypadku rzeczy w czasie rzeczywistym
RozzA
8
Jestem w 99% pewien, że twoje twierdzenie setIntervaljest błędne @RozzA - nadal podlega takim samym opóźnieniom, jak każdy inny JavaScript, a prawie każda przeglądarka również ustawia interwał na 4 ms. Czy masz link do posta na ten temat czy coś?
gnarf
9

To jest mój sposób na zrobienie tego, używam 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);
    }
};

Stosowanie:

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

timer.set_interval(500);

timer.stop();
Jaapze
źródło
+1, nieznacznie zmodyfikowałem to dla swoich celów, więc mogę używać wielu zmiennych interwałów - jsfiddle.net/h70mzvdq
Dallas
Zmodyfikowano także set_intervalfunkcję, aby NIE rozpoczynać nowego wykonania, chyba że nowy interwał jest mniejszy niż stary ..if (iv < this.iv) { clearInterval(this.timeout); this.start(false, iv); } else { this.iv = iv; }
Dallas
9

O wiele prostszym sposobem byłoby posiadanie ifinstrukcji w odświeżonej funkcji i kontrolki do wykonywania polecenia w regularnych odstępach czasu. W poniższym przykładzie uruchamiam alert co 2 sekundy, a interwał ( intrv) można zmieniać dynamicznie ...

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

var refreshId = setInterval(function() {
  if(!(i%intrv)) {
    alert('run!');
  }
  i++;
}, 1000);
Kiril Cvetkov
źródło
To także mój ulubiony. mały, prosty, rozszerzalny.
facet mograbi
Chciałem rozwiązania zwalniającego timera, który mógłby zresetować swoją szybkość na podstawie zdarzeń aplikacji; to zaspokoiło tę potrzebę w prosty i doskonały sposób. Dziękuję Ci.
Andrew Brown,
Fajnie, ale też odpala z przerwami w chwilach, kiedy tego nie potrzebujesz ... też trochę nieczytelny. Z tych powodów wolałbym osobiście setTimeout.
Kokodoko
4

Można to zainicjować w dowolny sposób. Timeout to metoda, której używałem, aby utrzymać go na szczycie godziny.

Potrzebowałem każdej godziny, aby rozpocząć blok kodu o tej godzinie. Zacznie się to przy starcie serwera i będzie działało co godzinę. Zasadniczo początkowy bieg ma rozpocząć interwał w ciągu tej samej minuty. Więc w sekundę od init, uruchamiaj natychmiast, a następnie co 5 sekund.

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

Alternatywnie, jeśli chcesz, aby coś się wydarzyło na początku, a potem na zawsze w określonym interwale, możesz po prostu wywołać to w tym samym czasie, co setInterval. Na przykład:

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

Tutaj mamy to uruchomienie po raz pierwszy, a potem co godzinę.

Dgerena
źródło
3

Prosta odpowiedź brzmi: nie możesz zaktualizować interwału już utworzonego timera . (Nie ma tylko dwie funkcje setInterval/setTimeri clearInterval/clearTimertak mający timerIdmożna tylko wyłączyć go). Ale można się pewne rozwiązania. Spójrz na to repozytorium github .

vbarbarosh
źródło
2

Nie mogłem zsynchronizować i zmienić szybkości mojego setIntervals i miałem zamiar zadać pytanie. Ale myślę, że znalazłem sposób. Z pewnością należy to poprawić, ponieważ jestem początkującym. Dlatego z przyjemnością przeczytam Twoje komentarze / uwagi na ten temat.

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

Jeśli chcesz liczniki zacząć zwiększać po załadowaniu strony, umieścić counter1()i counter2()w foo()przed countingSpeednazywa. W przeciwnym razie speedwykonanie zajmuje milisekundy. EDYCJA: Krótsza odpowiedź.

BEAGMB
źródło
2
(function variableInterval() {
    //whatever needs to be done
    interval *= 2; //deal with your interval
    setTimeout(variableInterval, interval);
    //whatever needs to be done
})();

nie może być krótszy

mikakun
źródło
1

Jestem początkującym w javascript i nie znalazłem żadnej pomocy w poprzednich odpowiedziach (ale wiele dobrych pomysłów).
Ten fragment kodu poniżej przyspiesza (przyspieszenie> 1) lub zwalnia (przyspieszenie <1). Mam nadzieję, że może to pomóc niektórym osobom:

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

Z :

  • timer: wartość początkowa setInterval w ms (rosnąca lub malejąca)
  • refresh: czas przed timerobliczeniem nowej wartości . To jest długość kroku
  • factor: różnica między starą a następną timerwartością. To jest wysokość kroku
mquantin
źródło
1

Utwórz nową funkcję:

// 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
źródło
1

Oto kolejny sposób tworzenia zwalniającego / przyspieszającego timera interwałowego. Przedział jest mnożony przez współczynnik, aż do przekroczenia całkowitego czasu.

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
źródło
0
var counter = 15;
var interval = setTimeout(function(){
    // your interval code here
    window.counter = dynamicValue;
    interval();
}, counter);
Hamidreza
źródło
daj mi błąd: Uncaught TypeError: interval is not a functionale to zadziałało: jsfiddle.net/fgs5nwgn
Nabi KAZ
0

Zainspirowany powyższym wewnętrznym wywołaniem zwrotnym , utworzyłem funkcję wywołującą wywołanie zwrotne w ułamkach minut. Jeśli limit czasu jest ustawiony na interwały takie jak 6 000, 15 000, 30 000, 60 000, będzie stale dostosowywał interwały zsynchronizowane z dokładnym przejściem do następnej minuty zegara systemowego.

//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
źródło