javascript - Vad är skillnaden mellan rekursiv setTimeout och setInterval?


Såvitt jag kan avgöra beter sig dessa två javascriptbitar på samma sätt:

Alternativ A:

function myTimeoutFunction()
{
    doStuff();
    setTimeout(myTimeoutFunction, 1000);
}

myTimeoutFunction();

Alternativ B:

function myTimeoutFunction()
{
    doStuff();
}

myTimeoutFunction();
setInterval(myTimeoutFunction, 1000);

Finns det någon skillnad mellan att använda setTimeout och setInterval?

Svar #1

De försöker i princip att göra samma sak, men setInterval-metoden kommer att vara mer exakt än setTimeout-metoden, eftersom setTimeout väntar 1000 ms, kör funktionen och sedan ställer in en ny timeout. Så väntetiden är faktiskt lite mer än 1000 ms (eller mycket mer om din funktion tar lång tid att köra).

Även om man kan tro att setInterval kommer att köras exakt var 1000 ms, är det viktigt att notera att setInterval också kan fördröjas, eftersom JavaScript inte är ett flertrådigt språk, vilket betyder att - om det finns andra delar av skriptet som körs - måste intervallet vänta på att dessa ska slutföras.

I denna Fiddle kan du tydligt se att timeouten kommer att ligga efter, medan intervallet nästan hela tiden är på nästan 1 anrop/sekund (vilket skriptet försöker göra). Om du ändrar variabeln speed högst upp till något litet som 20 (vilket betyder att den kommer att försöka köra 50 gånger per sekund), kommer intervallet aldrig riktigt att nå ett genomsnitt på 50 iterationer per sekund.

Fördröjningen är nästan alltid försumbar, men om du programmerar något riktigt precist bör du använda en självjusterande timer (vilket i princip är en timeout-baserad timer som ständigt justerar sig själv för den fördröjning den har skapat)

Svar #2

Finns det någon skillnad?

Ja. En Timeout körs efter en viss tid när setTimeout() anropas; ett Interval körs efter en viss tid efter att det föregående intervallet kördes.

Du kommer att märka skillnaden om din doStuff()-funktion tar en stund att köra. Till exempel, om vi representerar ett anrop till setTimeout/setInterval med ., ett utlösande av timeout/interval med * och JavaScript-kodexekvering med [-----], ser tidslinjerna ut så här:

Timeout:

.    *  .    *  .    *  .    *  .
     [--]    [--]    [--]    [--]

Interval:

.    *    *    *    *    *    *
     [--] [--] [--] [--] [--] [--]

Nästa komplikation uppstår om ett intervall triggas medan JavaScript redan är upptaget med något annat (till exempel hantering av ett tidigare intervall). I detta fall kommas intervallet ihåg och sker så snart den tidigare hanteraren avslutas och återlämnar kontrollen till webbläsaren. Så till exempel för en doStuff()-process som ibland är kort ([-]) och ibland lång ([-----]):

.    *    *    •    *    •    *    *
     [-]  [-----][-][-----][-][-]  [-]

• representerar ett intervallet som utlösts men inte kunde köra sin kod direkt, och istället gjordes väntande.

Så intervaller försöker 'komma ikapp' för att komma tillbaka i tidsschemat. Men de köar inte på varandra: det kan bara finnas en väntande körning per intervall. (Om de alla köade upp, skulle webbläsaren bli kvar med en ständigt växande lista av utestående körningar!)

.    *    •    •    x    •    •    x
     [------][------][------][------]

x representerar en intervallutsändning som inte kunde utföras eller göras väntande, så den kasserades istället.

Om din doStuff()-funktion vanemässigt tar längre tid att köra än det intervall som är inställt för den, kommer webbläsaren att använda 100 % CPU för att försöka hantera den och kan bli mindre responsiv.

Vilken använder du och varför?

Chained-Timeout ger en garanterad tidsslot av ledig tid till webbläsaren; Interval försöker säkerställa att funktionen den kör genomförs så nära sina schemalagda tider som möjligt, på bekostnad av webbläsarens UI-tillgänglighet.

Jag skulle överväga ett intervall för engångsanimeringar som jag ville skulle vara så mjuka som möjligt, medan kedjade timeout är mer lämpliga för pågående animationer som skulle ske hela tiden medan sidan är laddad. För mindre krävande användningar (som en trivial uppdaterare som avfyrar var 30:e sekund eller något liknande) kan du säkert använda båda.

När det gäller webbläsarkompatibilitet föregår setTimeout setInterval, men alla webbläsare du möter idag stödjer båda. Den sista eftersläntraren under många år var IE Mobile i WinMo <6.5, men förhoppningsvis är även det nu bakom oss.

Svar #3

setInterval()

setInterval() är en metod för kodexekvering baserad på tidsintervall som har den inbyggda förmågan att upprepade gånger köra ett specificerat skript när intervallet uppnås. Det bör inte nästlas in i sin callback-funktion av skriptförfattaren för att få det att loopa, eftersom det loopar som standard. Det kommer fortsätta att köras vid intervallet tills du anropar clearInterval().

Om du vill loopa kod för animationer eller vid en klocktick, använd då setInterval().

function doStuff() {
    alert("run your code here when time interval is reached");
}
var myTimer = setInterval(doStuff, 5000);

setTimeout()

setTimeout() är en tidsbaserad kodexekveringsmetod som endast kommer att köra ett skript en gång när intervallet nås. Det kommer inte att upprepas igen om du inte ställer in det för att loopa skriptet genom att nästla setTimeout()-objektet inuti funktionen det anropar för att köra. Om det är inställt för att loopa kommer det att fortsätta köras vid intervallet tills du anropar clearTimeout().

function doStuff() {
    alert("run your code here when time interval is reached");
}
var myTimer = setTimeout(doStuff, 5000);

Om du vill att något ska hända en gång efter en angiven tidsperiod, använd då setTimeout(). Det är för att det endast körs en gång när den angivna tidsintervallet uppnås.

Svar #4

setInterval gör det lättare att avbryta framtida körning av din kod. Om du använder setTimeout måste du hålla reda på timer-id om du vill avbryta det senare.

var timerId = null;
function myTimeoutFunction()
{
    doStuff();
    timerId = setTimeout(myTimeoutFunction, 1000);
}

myTimeoutFunction();

// later on...
clearTimeout(timerId);

mot

function myTimeoutFunction()
{
    doStuff();
}

myTimeoutFunction();
var timerId = setInterval(myTimeoutFunction, 1000);

// later on...
clearInterval(timerId);

Svar #5

Jag tycker att setTimeout-metoden är lättare att använda om du vill avbryta timeouten:

function myTimeoutFunction() {
   doStuff();
   if (stillrunning) {
      setTimeout(myTimeoutFunction, 1000);
   }
}

myTimeoutFunction();

Dessutom, om något skulle gå fel i funktionen kommer den bara sluta upprepa vid första gången ett fel uppstår, istället för att upprepa felet varje sekund.

Svar #6

Själva skillnaden ligger i deras syften.

setInterval()
   -> executes a function, over and over again, at specified time intervals  

setTimeout()
   -> executes a function, once, after waiting a specified number of milliseconds

Det är så enkelt

Mer utförliga detaljer här http://javascript.info/tutorial/settimeout-setinterval

Svar #7

När du kör någon funktion inuti setInterval, som tar längre tid än timeout -> kommer webbläsaren att fastna.

- T.ex., doStuff() tar 1500 sekunder att köras och du gör: setInterval(doStuff, 1000);
1) Webbläsaren kör doStuff() som tar 1,5 sekunder att utföra;
2) Efter ~1 sekund försöker den köra doStuff() igen. Men föregående doStuff() är fortfarande under körning -> så webbläsaren lägger till denna körning i kön (för att köras efter att den första är klar).
3,4,..) Samma läggs till i kö för körning för nästa iterationer, men doStuff() från tidigare är fortfarande pågående...
Som resultat blir webbläsaren fast.

För att förhindra detta beteende är det bästa sättet att köra setTimeout inuti setTimeout för att efterlikna setInterval.
För att korrigera tidsfördröjningar mellan setTimeout-anrop kan du använda ett själv-korrigerande alternativ till JavaScripts setInterval-teknik.

Svar #8

Din kod kommer att ha olika exekveringsintervall, och i vissa projekt, såsom online-spel, är det inte acceptabelt. Först, vad bör du göra för att få din kod att fungera med samma intervall, du bör ändra "myTimeoutFunction" till detta:

function myTimeoutFunction()
{
    setTimeout(myTimeoutFunction, 1000);
    doStuff();
}
myTimeoutFunction()

Efter denna ändring kommer det att vara lika med

function myTimeoutFunction()
{
    doStuff();
}
myTimeoutFunction();
setInterval(myTimeoutFunction, 1000);

Men du kommer fortfarande inte att ha ett stabilt resultat, eftersom JS är single-threaded. För tillfället, om JS-tråden är upptagen med något, kommer den inte att kunna köra din callback-funktion, och körningen kommer att skjutas upp med 2-3 ms. Om du har 60 körningar per sekund, och varje gång har du en slumpmässig 1-3 s fördröjning, kommer det att vara helt oacceptabelt (efter en minut kommer det att vara runt 7200 ms fördröjning), och jag kan rekommendera att använda något liknande detta:

    function Timer(clb, timeout) {
        this.clb = clb;
        this.timeout = timeout;
        this.stopTimeout = null;
        this.precision = -1;
    }

    Timer.prototype.start = function() {
        var me = this;
        var now = new Date();
        if(me.precision === -1) {
            me.precision = now.getTime();
        }
        me.stopTimeout = setTimeout(function(){
            me.start()
        }, me.precision - now.getTime() + me.timeout);
        me.precision += me.timeout;
        me.clb();
    };

    Timer.prototype.stop = function() {
        clearTimeout(this.stopTimeout);
        this.precision = -1;
    };

    function myTimeoutFunction()
    {
        doStuff();
    }

    var timer = new Timer(myTimeoutFunction, 1000);
    timer.start();

Denna kod kommer att garantera en stabil exekveringsperiod. Även om tråden är upptagen, kommer din kod att köras efter 1005 millisekunder, nästa gång kommer den att ha timeout efter 995 millisekunder, och resultatet kommer att vara stabilt.

Relaterade artiklar