Temporizatoare în Javascript (setInterval, setTimeout). Despre cum funcționează temporizatoarele JavaScript Repetați acțiunile la un interval de timp specificat javascript

Acasă / Sisteme de operare

Timeout JavaScript este o funcție nativă javascript care execută o bucată de cod după o întârziere specificată (în milisecunde). Acest lucru poate fi util atunci când trebuie să afișați o fereastră pop-up după ce utilizatorul a petrecut ceva timp pe pagina dvs. Sau doriți ca efectul când treceți cu mouse-ul peste un element să înceapă numai după ce a trecut ceva timp? În acest fel, puteți evita declanșarea neintenționată a unui efect dacă utilizatorul a trecut accidental peste el.

Exemplu simplu de setTimeout

Pentru a demonstra cum funcționează această caracteristică, vă sugerez să aruncați o privire la următoarea demonstrație, în care apare o fereastră pop-up la două secunde după ce se face clic pe butonul.

Vezi demonstrația

Sintaxă

Documentația MDN oferă următoarea sintaxă pentru setTimeout:

var timeoutID = window.setTimeout(func, ); var timeoutID = window.setTimeout(cod, );

  • timeoutID – un ID numeric care poate fi folosit în combinație cu clearTimeout() pentru a dezactiva cronometrul;
  • func – funcție de executat;
  • cod (în sintaxă alternativă) – linie de cod de executat;
  • întârziere – durata întârzierii în milisecunde, după care funcția va fi lansată. Valoarea implicită este 0.
setTimeout vs window.setTimeout

Sintaxa de mai sus folosește window.setTimeout . De ce?

De fapt, setTimeout și window.setTimeout sunt practic aceeași funcție. Singura diferență este că în a doua expresie folosim metoda setTimeout ca proprietate a obiectului fereastră globală.

Personal, cred că acest lucru face codul mult mai complicat. Dacă definim metoda alternativa Un timeout JavaScript care ar putea fi găsit și returnat cu prioritate ar avea și mai multe probleme.

În acest tutorial, nu vreau să mă încurc cu obiectul fereastră, dar, în general, depinde de tine să decizi ce sintaxă să folosești.

Exemple de utilizare

Acesta ar putea fi numele funcției:

funcția explode())( alert("Boom!"); ) setTimeout(explode, 2000);

Variabila care se refera la functie:

var explode = function())( alert("Boom!"); ); setTimeout(explode, 2000);

Sau o funcție anonimă:

setTimeout(function())( alert("Boom!"); ), 2000);

  • Un astfel de cod este prost înțeles și, prin urmare, va fi dificil de modernizat sau depanat;
  • Implica utilizarea metodei eval(), care ar putea fi o potentiala vulnerabilitate;
  • Această metodă este mai lentă decât altele, deoarece necesită rularea interpretului JavaScript.

De asemenea, rețineți că folosim metoda de alertă JavaScript timeout pentru a testa codul.

Transmiterea parametrilor către setTimout

În prima opțiune (și cross-browser), trecem parametrii funcției de apel invers, executată folosind setTimeout.

În exemplul următor, extragem un salut aleator din matricea de salutări și îl transmitem ca parametru funcției greet(), care este executată de setTimeout cu o întârziere de 1 secundă:

function greet(greeting)( console.log(greeting); ) function getRandom(arr)( return arr; ) var salutare = ["Buna ziua", "Bonjour", "Guten Tag"], randomGreeting = getRandom(salut); setTimeout(function())( salut(aleatoriu); ), 1000);

Vezi demonstrația

Metodă alternativă

În sintaxa dată la începutul articolului, există o altă metodă prin care puteți trece parametrii funcției de apel invers executată de JavaScript timeout. Această metodă implică ieșirea tuturor parametrilor după întârziere.

Pe baza exemplului anterior, obținem:

setTimeout(salut, 1000, salutare aleatorie);

Această metodă nu va funcționa în IE 9 și mai jos, unde parametrii trecuți sunt tratați ca nedefiniti. Dar există un polyfill special pe MDN pentru a rezolva această problemă.

Probleme conexe și „aceasta”

Codul executat de setTimeout rulează separat de funcția care l-a numit. Din această cauză, întâlnim anumite probleme care pot fi rezolvate folosind cuvântul cheie this.

var person = ( firstName: "Jim", introduce: function())( console.log ("Bună, eu"m " + this.firstName); ) ); person.introduce(); // Ieșiri: Bună, eu " m Jim setTimeout(persoana.introduce, 50); // Ieșiri: Bună, sunt nedefinit

Motivul pentru această ieșire este că în primul exemplu acesta indică obiectul person, iar în al doilea exemplu indică obiectul fereastră globală, care nu are o proprietate firstName.

Pentru a scăpa de această inconsecvență, puteți utiliza mai multe metode:

Forțați acest lucru să fie setat

Acest lucru se poate face folosind bind(), o metodă care creează o nouă funcție care, atunci când este apelată, folosește o anumită valoare ca valoare a cheii this. În cazul nostru, persoana specificată obiect. Acest lucru ne oferă în cele din urmă:

setTimeout(persoana.introduce.bind(persoana), 50);

Notă: metoda bind a fost introdusă în ECMAScript 5, ceea ce înseamnă că va funcționa numai în browsere moderne. În altele, atunci când îl utilizați, veți primi o eroare de execuție JavaScript „function timeout error”.

Utilizați biblioteca

Multe biblioteci includ funcții încorporate necesare pentru a rezolva această problemă. De exemplu, metoda jQuery.proxy(). Este nevoie de o funcție și returnează una nouă care va folosi întotdeauna un context specific. În cazul nostru, contextul va fi:

setTimeout($.proxy(persoană.introduce, persoană), 50);

Vezi demonstrația

Dezactivarea cronometrului

Valoarea returnată a setTimeout este un id numeric care poate fi folosit pentru a dezactiva cronometrul folosind funcția clearTimeout():

var timer = setTimeout(myFunction, 3000); clearTimeout(temporizator);

Să-l vedem în acțiune. În exemplul următor, dacă faceți clic pe butonul „Începe numărătoarea inversă”, numărătoarea inversă va începe. După ce este finalizat, pisoii îi vor primi pe al lor. Dar dacă faceți clic pe butonul „Opriți numărătoarea inversă”, temporizatorul JavaScript va fi oprit și resetat.

Vezi exemplu

Să rezumam

setTimeout este o funcție asincronă, ceea ce înseamnă că apelul rezultat la această funcție intră într-o coadă și va fi executat numai după ce toate celelalte acțiuni din stivă s-au finalizat. Nu poate rula simultan cu alte funcții sau cu un fir separat.

Metoda setInterval(), oferită pe interfețele Window și Worker, apelează în mod repetat o funcție sau execută un fragment de cod, cu o întârziere fixă ​​între fiecare apel.

Returnează un ID de interval care identifică în mod unic intervalul, astfel încât să îl puteți elimina mai târziu apelând clearInterval() . Această metodă este definită de mixin-ul WindowOrWorkerGlobalScope. Sintaxă = var intervalID domeniul de aplicare .setInterval(, func, [întârziere, arg1, ...]); Sintaxă = var intervalID domeniul de aplicare arg2, func cod ); Parametri func O funcție care trebuie executată la fiecare întârziere în milisecunde. Funcției nu i se transmite niciun argument și nu se așteaptă nicio valoare returnată. din aceleași motive care fac din utilizarea eval() un risc de securitate. .setInterval( delay Timpul, în milisecunde (mii de secundă), temporizatorul ar trebui să întârzie între execuțiile funcției sau codului specificat. Vedeți mai jos pentru detalii despre intervalul permis de valori de întârziere.

arg1, ..., argN Opțional Argumente suplimentare care sunt transmise funcției specificate de odată ce cronometrul expiră. Notă: trecerea de argumente suplimentare setInterval() în prima sintaxă nu funcționează

Internet Explorer

9 și mai devreme. Dacă doriți să activați această funcționalitate pe acel browser, trebuie să utilizați o completare polivalentă (vezi secțiunea).

Valoarea returnată

IntervalID returnat este o valoare numerică, diferită de zero, care identifică temporizatorul creat de apelul la setInterval() ; această valoare poate fi transmisă pentru a anula timeout-ul.

Poate fi util să știți că setInterval() și setTimeout() au același grup de ID-uri și că clearInterval() și clearTimeout() pot fi folosite din punct de vedere tehnic în mod interschimbabil. Pentru claritate, totuși, ar trebui să încercați să le potriviți întotdeauna pentru a evita confuzia atunci când vă mențineți codul.

Notă: Argumentul de întârziere este convertit într-un număr întreg semnat pe 32 de biți. Acest lucru limitează efectiv întârzierea la 2147483647 ms, deoarece este specificat ca un întreg semnat în IDL.

Exemple Exemplul 1: Sintaxă de bază

Următorul exemplu demonstrează sintaxa de bază setInterval() ".

Var intervalID = window.setInterval(myCallback, 500, „Parametrul 1”, „Parametrul 2”); function myCallback(a, b) ( // Codul dvs. aici // Parametrii sunt pur optionali. console.log(a); console.log(b); )

Exemplul 2: Alternarea a două culori

Următorul exemplu apelează funcția flashtext() o dată pe secundă până când este apăsat butonul Stop.

exemplu setInterval/clearInterval var nIntervId;

function changeColor() ( nIntervId = setInterval(flashText, 1000); ) function flashText() ( var oElem = document.getElementById("my_box"); oElem.style.color = oElem.style.color == "rosu" ? " albastru": "roșu"; // oElem.style.color == "roșu" ? "albastru": "roșu" este un operator ternar. ) funcția stopTextColor() ( clearInterval(nIntervId); )

Salut Lume

JavaScript Machinewriter - MDN Exemplu de funcție Typewriter (sSelector, nRate) ( function clean () ( clearInterval(nIntervId); bTyping = false; bStart = true; oCurrent = null; aSheets.length = nIdx = 0; ) function scroll (oSheet, nPos , bEraseAndStop) ( dacă (!oSheet.hasOwnProperty(„parts”) || aMap.length< nPos) { return true; } var oRel, bExit = false; if (aMap.length === nPos) { aMap.push(0); } while (aMap < oSheet.parts.length) { oRel = oSheet.parts]; scroll(oRel, nPos + 1, bEraseAndStop) ? aMap++ : bExit = true; if (bEraseAndStop && (oRel.ref.nodeType - 1 | 1) === 3 && oRel.ref.nodeValue) { bExit = true; oCurrent = oRel.ref; sPart = oCurrent.nodeValue; oCurrent.nodeValue = ""; } oSheet.ref.appendChild(oRel.ref); if (bExit) { return false; } } aMap.length--; return true; } function typewrite () { if (sPart.length === 0 && scroll(aSheets, 0, true) && nIdx++ === aSheets.length - 1) { clean(); return; } oCurrent.nodeValue += sPart.charAt(0); sPart = sPart.slice(1); } function Sheet (oNode) { this.ref = oNode; if (!oNode.hasChildNodes()) { return; } this.parts = Array.prototype.slice.call(oNode.childNodes); for (var nChild = 0; nChild < this.parts.length; nChild++) { oNode.removeChild(this.parts); this.parts = new Sheet(this.parts); } } var nIntervId, oCurrent = null, bTyping = false, bStart = true, nIdx = 0, sPart = "", aSheets = , aMap = ; this.rate = nRate || 100; this.play = function () { if (bTyping) { return; } if (bStart) { var aItems = document.querySelectorAll(sSelector); if (aItems.length === 0) { return; } for (var nItem = 0; nItem < aItems.length; nItem++) { aSheets.push(new Sheet(aItems)); /* Uncomment the following line if you have previously hidden your elements via CSS: */ // aItems.style.visibility = "visible"; } bStart = false; } nIntervId = setInterval(typewrite, this.rate); bTyping = true; }; this.pause = function () { clearInterval(nIntervId); bTyping = false; }; this.terminate = function () { oCurrent.nodeValue += sPart; sPart = ""; for (nIdx; nIdx < aSheets.length; scroll(aSheets, 0, false)); clean(); }; } /* usage: */ var oTWExample1 = new Typewriter(/* elements: */ "#article, h1, #info, #copyleft", /* frame rate (optional): */ 15); /* default frame rate is 100: */ var oTWExample2 = new Typewriter("#controls"); /* you can also change the frame rate value modifying the "rate" property; for example: */ // oTWExample2.rate = 150; onload = function () { oTWExample1.play(); oTWExample2.play(); }; span.intLink, a, a:visited { cursor: pointer; color: #000000; text-decoration: underline; } #info { width: 180px; height: 150px; float: right; background-color: #eeeeff; padding: 4px; overflow: auto; font-size: 12px; margin: 4px; border-radius: 5px; /* visibility: hidden; */ }

CopyLeft 2012 de la Mozilla Developer Network

[ Joacă | Pauză | Terminați]

Vivamus blandit massa ut metus mattis in fringilla lectus imperdiet. Proin ac ante a felis ornare vehicula. Fusce pellentesque lacus vitae eros convallis ut mollis magna pellentesque. Pellentesque placerat enim at lacus ultricies vitae facilisis nisi fringilla. In tincidunt tincidunt tincidunt.

Mașină de scris JavaScript

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam ultrices dolor ac dolor imperdiet ullamcorper. Suspendisse quam libero, luctus auctor mollis sed, malesuada condimentum magna. Quisque in ante tellus, in placerat est. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec a mi magna, quis mattis dolor. Etiam sit amet ligula quis urna auctor imperdiet nec faucibus ante. Mauris vel consectetur dolor. Nunc eget elit eget velit pulvinar fringilla consectetur aliquam purus. Curabitur convallis, justo posuere porta egestas, velit erat ornare torttor, non viverra justo diam eget arcu. Phasellus adipiscing fermentum nibh ac commodo. Nam turpis nunc, suscipit a hendrerit vitae, volutpat non ipsum.
Phasellus ac nisl lorem:

Duis lobortis sapien quis nisl luctus porttitor. In tempor sempre libero, eu tincidunt dolor eleifend sit amet. Ut nec velit in dolor tincidunt rhoncus non non diam. Morbi auctor ornare orci, non euismod felis gravida nec. Curabitur elementum nisi a eros rutrum nec blandit diam placerat. Aenean tincidunt risus ut nisi consectetur cursus. Ut vitae quam elit. Donec dignissim est in quam tempor consequat. Aliquam aliquam diam non felis convallis suscipit. Nulla facilitati. Donec lacus risus, dignissim et fringilla et, egestas vel eros. Duis malesuada accumsan dui, at fringilla mauris bibStartum quis. Cras adipiscing ultricies fermentum. Praesent bibStartum condimentum feugiat.

Nam faucibus, ligula eu fringilla pulvinar, lectus tellus iaculis nunc, vitae scelerisque metus leo non metus. Proin mattis lobortis lobortis. Quisque accumsan faucibus erat, vel varius torttor ultricies ac. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nec libero nunc. Nullam torttor nunc, elementum a consectetur et, ultrices eu orci. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque a nisl eu sem vehicula egestas.

Argumente de apel invers

După cum sa discutat anterior, versiunile Internet Explorer 9 și mai jos nu acceptă transmiterea de argumente către funcția de apel invers nici în setTimeout() sau setInterval() . Următorul cod specific IE demonstrează o metodă pentru a depăși această limitare. Pentru a utiliza, pur și simplu adăugați următorul cod în partea de sus a scriptului.

/*\ |*| |*| Polyfill specific IE care permite trecerea de argumente arbitrare la |*| funcțiile de apel invers ale temporizatoarelor javascript (sintaxă standard HTML5)..setInterval |*| https://site/Utilizator:fusionchess |*| |*| Sintaxă: |*| var timeoutID = window.setTimeout(func, delay[, arg1, arg2, ...]); |*| var timeoutID = window.setTimeout(cod, întârziere); |*| var intervalID = window.setInterval(func, delay[, arg1, arg2, ...]); |*| var intervalID = window.setInterval(cod, întârziere); |*| \*/ dacă (document.all && !window.setTimeout.isPolyfill) ( var __nativeST__ = window.setTimeout; window.setTimeout = funcție (vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */) ( var aArgs = Array .prototype.slice.call(argumente, 2); return __nativeST__(vCallback instanceof Function () (vCallback.apply(null, aArgs); ): vCallback, nDelay if (document.all && !window.setInterval.isPolyfill); ( var __nativeSI__ = window.setInterval; window.setInterval = funcție (vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */) ( var aArgs = Array.prototype. slice.call(arguments, 2); return __nativeSI__(vCallback) instanceof Funcție () ( vCallback.apply(null, aArgs); ): vCallback, nDelay );

O altă posibilitate este să utilizați o funcție anonimă pentru a vă apela înapoi, deși această soluție este puțin mai scumpă. Exemplu:

Var intervalID = setInterval(function() ( myFunc("unu", "doua", "trei"); ), 1000);

var intervalID = setInterval(function(arg1) ().bind(undefined, 10), 1000);

File inactive Necesită Gecko 5.0 (Firefox 5.0 / Thunderbird 5.0 / SeaMonkey 2.2)

Începând cu Gecko 5.0 (Firefox 5.0 / Thunderbird 5.0 / SeaMonkey 2.2), intervalele sunt fixate pentru a se declanșa nu mai des de o dată pe secundă în filele inactive.

Problema "aceasta".

Când treceți o metodă setInterval() sau orice altă funcție, aceasta este invocată cu această valoare greșită. Această problemă este explicată în detaliu în referința JavaScript.

Explicaţie

MyArray = ["zero", "unu", "două"]; myArray.myMethod = function (sProperty) ( alert(arguments.length > 0 ? this : this); ); myArray.myMethod(); // afișează „zero,unu,dou” myArray.myMethod(1); // afișează „one” setTimeout(myArray.myMethod, 1000); // afișează „” după 1 secundă setTimeout(myArray.myMethod, 1500, „1”); // se afișează „nedefinit” după 1,5 secunde // trecerea obiectului „acest” cu .call nu va funcționa // deoarece acest lucru va schimba valoarea acestui însuși setTimeout // în timp ce dorim să schimbăm valoarea acestuia în myArray .myMethod // de fapt, va fi o eroare deoarece codul setTimeout se așteaptă ca acesta să fie obiectul fereastră: setTimeout.call(myArray, myArray.myMethod, 2000 // eroare: "NS_ERROR_XPC_BAD_OP_ON_WN_PROTO: Operație ilegală pe obiectul prototip WrappedNative"); setTimeout.call(myArray, myArray.myMethod, 2500, 2);

După cum puteți vedea, nu există modalități de a trece acest obiect la funcția de apel invers în JavaScript moștenit.

O posibila solutie

O posibilă modalitate de a rezolva problema „această” este înlocuirea celor două funcții globale setTimeout() sau setInterval() native cu două non-nativ cele care permit invocarea lor prin metoda Function.prototype.call. Următorul exemplu arată o posibilă înlocuire:

// Activează trecerea obiectului „acest” prin temporizatoarele JavaScript var __nativeST__ = window.setTimeout, __nativeSI__ = window.setInterval; window.setTimeout = funcție (vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */) ( var oThis = this, aArgs = Array.prototype.slice.call(arguments, 2); return __nativeST__(vCallback instanceof Function ? () ( vCallback.apply(oThis, aArgs); ): vCallback, nDelay ); window.setInterval = funcție (vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */) ( var oThis = this, aArgs = Array.prototype.slice.call(arguments, 2); return __nativeSI__(vCallback instanceof Function ? () ( vCallback.apply(oThis, aArgs); ): vCallback, nDelay );

Aceste două înlocuiri permit, de asemenea, trecerea standard HTML5 a argumentelor arbitrare la funcțiile de apel invers ale temporizatoarelor din IE. Deci pot fi folosite ca neconforme cu standardele de asemenea poliumpluturi. Vezi pentru a conform cu standardele poliumplutură.

Test de caracteristici noi:

MyArray = ["zero", "unu", "două"]; myArray.myMethod = function (sProperty) ( alert(arguments.length > 0 ? this : this); ); setTimeout(alertă, 1500, „Bună lume!”); // se păstrează utilizarea standard a setTimeout și setInterval, dar... setTimeout.call(myArray, myArray.myMethod, 2000); // afișează „zero,one,two” după 2 secunde setTimeout.call(myArray, myArray.myMethod, 2500, 2); // afișează „două” după 2,5 secunde

Pentru o versiune mai complexă, dar totuși modulară a acesteia ( Daemon) vezi Managementul demonilor JavaScript. Această versiune mai complexă nu este altceva decât o colecție mare și scalabilă de metode pentru Daemon constructor. Cu toate acestea, cel Daemon constructorul în sine nu este altceva decât o clonă a MiniDaemon cu un suport suplimentar pentru initşi la pornire funcţii declarate în timpul instanţierii demonul. Deci MiniDaemon framework rămâne modalitatea recomandată pentru animațiile simple, deoarece Daemon fără colecția sa de metode este în esență o clonă a acesteia.

minidaemon.js /*\ |*| |*| :: MiniDaemon:: |*| |*| Revizia #2 - 26 septembrie 2014.setInterval |*| https://site/Utilizator:fusionchess |*| https://github.com/madmurphy/minidaemon.js |*| |*| Acest cadru este lansat sub Licența Publică Generală Inferioară GNU, versiunea 3 sau mai recentă. |*| http://www.gnu.org/licenses/lgpl-3.0.html |*| \*/ funcția MiniDaemon (oOwner, fTask, nRate, nLen) ( if (!(aceasta și& această instanță a MiniDaemon)) ( return; ) if (arguments.length< 2) { throw new TypeError("MiniDaemon - not enough arguments"); } if (oOwner) { this.owner = oOwner; } this.task = fTask; if (isFinite(nRate) && nRate >0) ( this.rate = Math.floor(nRate); ) if (nLen > 0) ( this.length = Math.floor(nLen); ) ) MiniDaemon.prototype.owner = null; MiniDaemon.prototype.task = nul; MiniDaemon.prototype.rate = 100; MiniDaemon.prototype.length = Infinit;< 1: this.INDEX + 1 >aceasta.lungime; ); MiniDaemon.prototype.synchronize = function () ( if (this.PAUSED) ( return; ) clearInterval(this.SESSION); this.SESSION = setInterval(MiniDaemon.forceCall, this.rate, this); ); MiniDaemon.prototype.pause = function () ( clearInterval(this.SESSION); this.PAUSED = true; ); MiniDaemon.prototype.start = funcție (bReverse) ( var bBackw = Boolean(bReverse); if (this.BACKW === bBackw && (this.isAtEnd() || !this.PAUSED)) ( return; ) this.BACKW = bBackw; this.PAUSED = false.synchronize();

MiniDaemon transmite argumente funcției de apel invers. Dacă doriți să lucrați la el cu browsere care în mod nativ nu acceptă această caracteristică, utilizați una dintre metodele propuse mai sus.

Sintaxă

var myDaemon = new MiniDaemon( acestObiect, sună din nou[ , rata [, lungime]]);

Descriere Note de utilizare

Funcția setInterval() este folosită în mod obișnuit pentru a seta o întârziere pentru funcțiile care sunt executate din nou și din nou, cum ar fi animațiile. Puteți anula intervalul folosind WindowOrWorkerGlobalScope.clearInterval() .

Dacă doriți să vă apelați funcția dată după întârzierea specificată, utilizați .

Restricții de întârziere

Este posibil ca intervalele să fie imbricate; adică callback-ul pentru setInterval() poate, la rândul său, apela setInterval() pentru a începe să ruleze un alt interval, chiar dacă primul este încă în curs. Pentru a atenua impactul potențial pe performanță, odată ce intervalele sunt imbricate peste cinci niveluri, browserul va aplica automat o valoare minimă de 4 ms pentru interval. Încercările de a specifica o valoare mai mică de 4 ms în apelurile imbricate profund la setInterval() vor fi fixate la 4 ms.

Browserele pot impune valori minime și mai stricte pentru interval în anumite circumstanțe, deși acestea nu ar trebui să fie comune. Rețineți, de asemenea, că timpul real care se scurge între apeluri la apel invers poate fi mai mare decât întârzierea dată; vedeți Motive pentru întârzieri mai mari decât cele specificate în WindowOrWorkerGlobalScope.setTimeout() pentru exemple.

Asigurați-vă că durata execuției este mai mică decât frecvența intervalului

Dacă există posibilitatea ca logica dvs. să dureze mai mult decât intervalul de timp, este recomandat să apelați recursiv o funcție numită folosind setTimeout() . De exemplu, dacă utilizați setInterval() pentru a interoga un server la distanță la fiecare 5 secunde, latența rețelei, un server care nu răspunde și o serie de alte probleme ar putea împiedica finalizarea cererii în timpul alocat. Ca atare, s-ar putea să vă aflați cu solicitări XHR puse în coadă care nu vor reveni neapărat în ordine.

Este extrem de important să înțelegeți cum funcționează temporizatoarele JavaScript. Adesea, comportamentul lor nu se potrivește cu înțelegerea noastră intuitivă a multithreading-ului și acest lucru se datorează faptului că, în realitate, sunt executate într-un singur thread. Să ne uităm la patru funcții cu care putem gestiona temporizatoarele:

  • var id = setTimeout(fn, delay); - Creează un temporizator simplu care va apela o anumită funcție după o anumită întârziere. Funcția returnează un ID unic cu care cronometrul poate fi întrerupt.
  • var id = setInterval(fn, delay); - Similar cu setTimeout, dar apelează continuu funcția la un interval dat (până la oprire).
  • clearInterval(id);, clearTimeout(id); - Acceptă un ID de cronometru (returnat de una dintre funcțiile descrise mai sus) și oprește execuția apelului invers"a.
Ideea principală de luat în considerare este că acuratețea perioadei de întârziere a temporizatorului nu este garantată. Pentru început, browserul execută toate evenimentele JavaScript asincrone într-un singur fir (cum ar fi clicurile mouse-ului sau temporizatoarele) și numai în momentul în care este rândul evenimentului respectiv. Acest lucru este cel mai bine demonstrat de următoarea diagramă:

Există destul de multe informații de preluat în această figură, dar înțelegerea acesteia vă va oferi o înțelegere mai profundă a modului în care funcționează asincronia JavaScript. Acest grafic reprezintă timpul pe verticală în milisecunde, blocurile albastre arată blocurile de cod JavaScript care au fost executate. De exemplu, primul bloc se execută în medie în 18 ms, un clic de mouse blochează execuția pentru aproximativ 11 ms etc.

JavaScript poate executa doar o bucată de cod (datorită naturii execuției cu un singur fir), fiecare blocând executarea altor evenimente asincrone. Aceasta înseamnă că atunci când are loc un eveniment asincron (cum ar fi un clic de mouse, un apel de temporizator sau finalizarea unei cereri XMLHttp), acesta este adăugat la o coadă și executat mai târziu (implementarea variază în funcție de browser, desigur, dar să fim de acord cu numiți-o „coadă”).

Pentru început, să ne imaginăm că în interiorul unui bloc JavaScript pornesc două temporizatoare: setTimeout cu o întârziere de 10 ms și setInterval cu aceeași întârziere. În funcție de momentul începerii cronometrului, acesta se va declanșa în momentul în care nu am finalizat încă primul bloc de cod. Rețineți, totuși, că nu se declanșează imediat (acest lucru nu este posibil din cauza firului unic). În schimb, funcția amânată este pusă în coadă și executată în următorul moment disponibil.

De asemenea, în timpul execuției primului bloc JavaScript, apare un clic de mouse. Handler-ul pentru acest eveniment asincron (și este asincron pentru că nu îl putem prezice) nu poate fi executat imediat în acest moment, așa că ajunge și el într-o coadă, ca și cronometrul.

După ce primul bloc de cod JavaScript a fost executat, browserul pune întrebarea „Ce așteaptă să fie executat?” ÎN în acest caz, Controlul de clic al mouse-ului și temporizatorul sunt într-o stare în așteptare. Browserul selectează unul dintre ele (managerul de clic) și îl execută. Cronometrul va aștepta următoarea bucată de timp disponibilă în coada de execuție.

Rețineți că, în timp ce gestionarea clicurilor mouse-ului se execută, se declanșează primul interval de apel invers. La fel ca timer-callback, va fi pus în coadă. Cu toate acestea, rețineți că atunci când intervalul se declanșează din nou (în timp ce timer-callback rulează), acesta va fi eliminat din coadă. Dacă toate apelurile de tip interval ar fi puse în coadă în timp ce se execută o bucată mare de cod, aceasta ar avea ca rezultat o grămadă de funcții care așteaptă să fie apelate, fără perioade de întârziere între execuția lor. În schimb, browserele tind să aștepte până când nu mai există funcții lăsat în coadă înainte de a adăuga încă unul la coadă.

Astfel, putem observa cazul în care a treia declanșare a intervalului-callback coincide cu momentul în care acesta este deja executat. Acest lucru ilustrează un punct important: intervalelor nu le pasă ce rulează în prezent, ele vor fi adăugate la coadă fără a ține cont de perioada de întârziere dintre execuții.

În cele din urmă, după încheierea celui de-al doilea interval-callback, vom vedea că nu mai este nimic pentru a fi executat de motorul JavaScript. Aceasta înseamnă că browserul așteaptă din nou să apară noi evenimente asincrone. Acest lucru se va întâmpla la marcajul de 50 ms, unde interval-callback va funcționa din nou. În acest moment nu va exista nimic care să-l blocheze, așa că va funcționa imediat.

Să ne uităm la un exemplu care ilustrează frumos diferența dintre setTimeout și setInterval.
setTimeout(function())( /* Un bloc lung de cod... */ setTimeout(arguments.callee, 10); ), 10);
Aceste două opțiuni sunt echivalente la prima vedere, dar în realitate nu sunt. Codul care utilizează setTimeout va avea întotdeauna o întârziere de cel puțin 10 ms după apelul anterior (poate fi mai mult, dar niciodată mai puțin), în timp ce codul care utilizează setInterval va tinde să fie apelat la fiecare 10 ms, indiferent de momentul în care a avut loc apelul anterior.

Să rezumăm tot ce s-a spus mai sus:
- Motoarele JavaScript utilizează un mediu cu un singur thread, transformând evenimentele asincrone într-o coadă care așteaptă execuția,
- Funcțiile setTimeout și setInterval sunt executate fundamental diferit în codul asincron,
- Dacă cronometrul nu poate fi executat în în acest moment, va fi amânată până la următorul punct de execuție (care va fi mai mare decât întârzierea dorită),
- Intervalele (setInterval) pot fi executate unul după altul fără întârziere dacă executarea lor durează mai mult decât întârzierea specificată.

Toate acestea sunt extrem de informatii importante pentru dezvoltare. Cunoașterea modului în care funcționează motorul JavaScript, în special cu o mulțime de evenimente asincrone (ceea ce se întâmplă adesea), pune o bază excelentă pentru construirea de aplicații avansate.

În programarea în limbaje de scripting, există periodic necesitatea de a crea o pauză - de a întrerupe execuția programului pentru un timp și apoi de a continua lucrul. De exemplu, în scripturile VBS și PHP sunt posibile următoarele metode:

VBS: wscript.sleep 1500 (oprire timp de 1,5 secunde)

PHP: sleep(10); (oprire 10 secunde)

În timpul unor astfel de pauze, sistemul de rulare (PHP sau VBS) nu face nimic. Un dezvoltator care încearcă să folosească intuitiv ceva similar în Javascript va fi surprins neplăcut. O eroare tipică atunci când încercați să creați o pauză în Javascript arată astfel:

Funcția badtest() (pentru (var i=1; i< 10; i++) { window.setTimeout("document.getElementById("test1").value += " + i, 900) } }

Crezi că atunci când, în timpul buclei, vine rândul să trageți următorul număr, setTimeout-ul dvs. va opri sincer să funcționeze JavaScript, așteptați 0,9 secunde, adăugați numărul dorit la sfârșitul câmpului de introducere și apoi continuați să lucrați. Dar, în realitate, acesta nu este cazul: setInterval și setTimeout în Javascript întârzie doar execuția acțiunii (sau funcției) specificate în paranteze. În exemplul nostru, se va întâmpla următoarele:

  • i = 1;
  • întârzie adăugarea numărului „1” în câmpul de intrare cu 0,9 secunde;
  • Imediat după setarea acestei probleme, ciclul continuă: i=2;
  • întârzie adăugarea numărului „2” în câmpul de intrare cu 0,9 secunde;
  • Imediat înseamnă, de exemplu, 1 ms (adică disproporționat de mic față de 900 ms): bucla își va face treaba aproape instantaneu, creând mai multe sarcini amânate din același moment în timp. Aceasta înseamnă că toate sarcinile de „desen” în așteptare vor fi finalizate aproape în același timp, fără pauze între adăugarea de noi numere. Ciclul începe; totul îngheață timp de 0,9 s; și shirr - toate numerele sunt împușcate la rând unul după altul.

    Cum se aplică corect setTimeout într-un astfel de caz? E complicat. Va trebui să apelați funcția recursiv(din cadrul funcției aceeași funcție) și pentru ca acest proces să nu fie nesfârșit, setați o condiție de oprire (de exemplu, dimensiunea numărului care trebuie tipărit):

    Funcția welltest() (dacă (i< 9) { document.getElementById("test2").value += ++i window.setTimeout("welltest()", 400) } }

    Și variabila i va trebui să fie inițializată în afara funcției - de exemplu, astfel:

    Acum totul funcționează așa cum ar trebui (am redus timpul de întârziere de la 0,9 s la 0,4 s). Dar pentru astfel de sarcini, este mai logic să folosiți setInterval mai degrabă decât setTimeout (deși acest lucru va necesita două funcții):

    Funcția besttest() ( window.i = 0 window.timer1 = window.setInterval("draw()", 400) ) function draw() ( document.getElementById("test3").value += ++i if (i >= 9) clearInterval(window.timer1) )

    Particularitatea metodei Javascirpt setInterval este că nu trece „de la sine”, trebuie oprită cu o metodă specială clearInterval. Și pentru a clarifica ce anume trebuie oprit, sarcinii pentru acțiunea amânată i se atribuie un identificator special - un temporizator: window.timer1 = window.setInterval(...) .

    Identificatorii pot fi, de asemenea, alocați sarcinilor create prin metoda setTimeout. Toate ID-urile temporizatorului trebuie să fie distincte unele de altele (unice în fereastra curentă a browserului). Apoi puteți crea mai multe sarcini diferite în fereastră care folosesc acțiuni amânate, iar aceste sarcini vor fi executate în paralel (un fel de simultan, dacă computerul are suficiente resurse), ceea ce este practic imposibil în PHP sau VBS.

    Iată un exemplu de pagină cu mai multe temporizatoare Javascript care rulează simultan: setinterval.htm (funcții Javascript în fișierul setinterval.js). Toate temporizatoarele de pagină (cu excepția meniului) pot fi oprite folosind tasta Esc. Toate exemplele de cronometre se bazează pe o numărătoare inversă „naturală” (și nu abstractă i++) - timp sau distanță. Toate „ceasurile” sunt special desincronizate (pentru claritate). Cronometrele dependente de distanță sunt utilizate în „indicator” și în meniul drop-down („extras”).

    Meniu derulant

    Meniul nostru de alunecare este de fapt alunecat (de sub „antet”): spații sunt lăsate special între elemente, astfel încât să puteți vedea cum alunecă. În mod neașteptat, s-a dovedit că nu am putut face ieșirea la fel de lină pentru liste de lungimi diferite - probabil din cauza performanței scăzute a computerului (AMD Athlon 999 MHz).

    Este destul de evident că pentru frumusețe și armonie este necesar ca listele cu diferite elemente de meniu să apară în același timp. Adică, listele mai lungi ar trebui să renunțe cu mai multe de mare viteză, cele mai scurte - la o viteza mai mica. Se pare că ar putea fi implementat astfel:

  • Am setat timpul total de „plecare”, de exemplu, la 200 ms.
  • Dacă lista derulantă are o înălțime de 20 px, este evident că o putem deplasa în jos cu un pixel la un interval de 10 ms - și apoi în 200 ms va ieși întreaga listă.
  • Dacă meniul derulant are o înălțime de 40 de pixeli, pentru a se potrivi în aceeași perioadă de timp, trebuie să îl deplasăm în jos cu un pixel la fiecare 5 ms.
  • După această logică, dacă lista verticală are o înălțime de 200 de pixeli, ar trebui să o mutăm în jos cu un pixel la fiecare 1 ms. Dar o astfel de viteză nu funcționează pe computerul nostru - browserul pur și simplu nu are timp să deseneze noua poziție a listei într-o milisecundă. Da. Javascript reușește să numere (ce este acolo de numărat?), dar browserul (Firefox) nu are timp să se afișeze. Situație tipică pentru web.

    Prin urmare, este posibil să egalizați mai mult sau mai puțin ora de plecare a meniului numai cu ajutorul cârjelor și încă nu este clar cum va funcționa acest lucru pentru mai mult. calculator rapid. Dar ar trebui să ne bazăm pe cel mai lent, nu? Algoritmul (fără a ține cont de viteza computerului) rezultă cam așa:

  • Setați timpul total pentru verificarea listei: timp = 224 (ms).
  • Setăm timpul minim pentru un interval din ciclu: întârziere = 3 (ms).
  • Setați pasul minim pentru mutarea listei: offset = 1 (px).
  • Schimbăm toate acestea în funcție de înălțimea listei: 1) mărim timpul de întârziere (interval) invers proporțional cu înălțimea și direct proporțional cu timpul total de timp (la înălțimea de 224 coeficientul este 1); 2) dacă înălțimea este mai mare de 40 px, creșteți treapta minimă proporțional cu înălțimea. Constanta „40” a fost obținută experimental pentru cel mai lent computer. Testele pe un computer Pentium 4 CPU 2.53GHz au dezvăluit exact același număr - 40. În caz contrar, temporizatoarele se defectează, listele nu sunt în pas.
  • Acum listele apar mai mult sau mai puțin. Pentru timp mai mult sau mai puțin similar. Pe pagina setinterval.htm.

    Și iată că vine Bruce:

    Funcția slide_do(obj, maxtop, offset) ( if (getTopLeft(obj).top)< maxtop) { obj.style.top = getTopLeft(obj).top + offset } else { if (obj && obj.timer1) { clearInterval(obj.timer1) obj.timer1 = null } } }

    Funcția în sine, care împinge listele imbricate din meniu, este, după cum putem vedea, foarte simplă. Tot ce rămâne este să-l rulezi cu ceva de genul acesta:

    Ts.timer1 = setInterval(function())(slide_do(ts, maxtop, offset)), întârziere)

    Ei bine, înainte de a începe, calculați toate aceste maxtop și offset și, de asemenea, plasați lista în poziția mintop. Aceasta este ceea ce face funcția slide() „preliminară” de 40 de linii. Și toate împreună - în fișierul setinterval.js. Da, și porcăria asta nu va funcționa deloc fără fișierul de stiluri inclus

    • Din:
    • Inregistrat: 08.07.2014
    • Posturi: 3.896
    • Aprecieri: 497
    Subiect: SetTimeOut și SetInterval, care este mai bine de utilizat în JavaScript?

    Funcția setInterval este concepută pentru a rula codul de mai multe ori la intervale regulate. Cu toate acestea, are o serie de dezavantaje, în principal un comportament diferit în browsere diferite.

    Prima diferență este diferența de timp la care este setat temporizatorul pentru următoarea lansare. Să creăm un mic test: vom măsura timpul care a trecut de la începutul rulării anterioare și de la sfârșitul acesteia.

    var d1 = data noua(), d2 = data noua(); setInterval(function() ( var d = new Date(); document.body.innerHTML += (d - d1) + " " + (d - d2) + "
    "; // Pune un semn la începutul funcției d1 = new Date(); while (new Date() - d1< 200); // ничего не делаем 200 миллисекунд // И в конце функции d2 = new Date(); }, 1000);

    Ieșirea va fi informativă începând de la a doua linie.

    În Firefox, Opera, Safari și Chrome situația va fi similară: primul număr va fi aproximativ egal cu 1000, al doilea - cu 200 mai puțin. Singura diferență va fi în răspândirea valorilor. Cea mai mică diferență este în Chrome și Opera.

    2 Răspuns de PunBB (editat de PunBB 2017.06.08 16:45)
    • Din: Moscova, Sovkhoznay 3, ap. 98
    • Inregistrat: 08.07.2014
    • Posturi: 3.896
    • Aprecieri: 497

    O altă diferență care este mai puțin vizibilă și mai dificil de reprodus, dar poate provoca uneori multe probleme, este rezistența la schimbările de timp ale sistemului. Dacă rulați următorul test

    setInterval(function() ( document.body.innerHTML = Math.random(); ), 500);

    Și după pornire, setați ora sistemului cu un minut înapoi, apoi la Browsere Firefoxși Safari, schimbarea numărului se va întrerupe, iar după un minut va începe din nou. Desigur, traducerea manuală a timpului de sistem este o situație extrem de rară, dar multe sisteme sunt configurate să sincronizeze automat ora cu serverele de pe Internet, astfel încât în ​​unele situații acest factor nu poate fi redus.

    Un alt mic dezavantaj al funcției setInterval este că, pentru a putea opri acțiunea acesteia, trebuie să vă amintiți undeva identificatorul ei, ceea ce nu este întotdeauna convenabil.

    3 Răspuns de PunBB
    • Din: Moscova, Sovkhoznay 3, ap. 98
    • Inregistrat: 08.07.2014
    • Posturi: 3.896
    • Aprecieri: 497
    Re: SetTimeOut și SetInterval, care este mai bine de utilizat în JavaScript?

    Pentru a scăpa de dezavantajele enumerate ale setInterval, puteți utiliza mai multe setTimeout.

    O alternativă importantă la setInterval este setTimeout recursiv:

    /** în loc de: var timerId = setInterval(function() ( alert("tick"); ), 2000); */ var timerId = setTimeout(function tick() (alerta("tick"); timerId = setTimeout(tick, 2000); ), 2000);

    În codul de mai sus, următoarea execuție este programată imediat după terminarea celei anterioare.

    SetTimeout recursiv este o metodă de sincronizare mai flexibilă decât setInterval, deoarece timpul până la următoarea execuție poate fi programat în moduri diferite, în funcție de rezultatele celei curente.

    De exemplu, avem un serviciu care sonda serverul pentru date noi la fiecare 5 secunde. Dacă serverul este supraîncărcat, puteți crește intervalul de interogare la 10, 20, 60 de secunde... Și apoi îl întoarceți înapoi când totul revine la normal.

    Dacă rulăm în mod regulat sarcini care consumă intens CPU, atunci putem estima timpul petrecut cu execuția lor și putem planifica următoarea rulare mai devreme sau mai târziu.

    4 Răspuns de PunBB
    • Din: Moscova, Sovkhoznay 3, ap. 98
    • Inregistrat: 08.07.2014
    • Posturi: 3.896
    • Aprecieri: 497
    Re: SetTimeOut și SetInterval, care este mai bine de utilizat în JavaScript?

    SetTimeout recursiv garantează o pauză între apeluri, setInterval nu.

    Să comparăm cele două coduri. Primul folosește setInterval:

    var i = 1; setInterval(function() ( func(i); ), 100);

    Al doilea folosește un setTimeout recursiv:

    var i = 1; setTimeout(function run() ( func(i); setTimeout(run, 100); ), 100);

    Cu setInterval, temporizatorul intern se va declanșa exact la fiecare 100 ms și va apela funcția(i):

    Pauza reală dintre apelurile func cu setInterval este mai mică decât cea indicată în cod!

    Acest lucru este firesc, deoarece timpul de funcționare al funcției nu este luat în considerare în niciun fel, „mâncă” o parte din interval.

    De asemenea, este posibil ca func să se fi dovedit a fi mai complexă decât ne așteptam și a durat mai mult de 100 ms pentru a fi executat.

    În acest caz, interpretul va aștepta finalizarea funcției, apoi va verifica cronometrul și, dacă timpul pentru apelarea setInterval a venit deja (sau a trecut), atunci următorul apel va avea loc imediat.

    Dacă funcția rulează mai mult decât pauza setInterval, atunci apelurile vor avea loc fără nicio întrerupere.

    5 Răspuns de sempai
    • De la: Ierusalim
    • Inregistrat: 2015.06.02
    • Mesaje: 958
    • Aprecieri: 274
    Re: SetTimeOut și SetInterval, care este mai bine de utilizat în JavaScript?

    Totul depinde de sarcina la îndemână. Inițial, SetTimeOut este folosit pentru a porni cronometrul o dată, iar SetInterval este folosit pentru a porni o buclă. Dar ambele funcții pot fi folosite pentru a rula scripturi ciclic, dacă, de exemplu, le rulați recursiv în Funcții SetTimeOut, atunci va acționa aproape identic cu SetInterval.

    Dezavantajul SetInterval în acest moment este că nu ia în considerare timpul de execuție al script-ului (funcției) în sine și dacă, de exemplu, îl utilizați pentru interogări grele, atunci intervalul de timp va fi redus semnificativ și poate diferi în diferite browsere.

    Dar din nou, dacă funcția sau cererea este redusă la minimum, atunci utilizatorul final este puțin probabil să simtă diferența.
    Prin urmare, ce să folosească este la latitudinea fiecăruia să decidă singur.

    © 2024 ermake.ru -- Despre repararea PC-ului - Portal de informații