Časovače v Javascriptu (setInterval, setTimeout). O tom, jak fungují časovače JavaScriptu Opakujte akce v zadaném časovém intervalu javascript

Domov / Operační systémy

Časový limit JavaScriptu je nativní funkce javascriptu, která spustí část kódu po zadaném časovém zpoždění (v milisekundách). To může být užitečné, když potřebujete zobrazit vyskakovací okno poté, co uživatel strávil nějaký čas na vaší stránce. Nebo chcete, aby se efekt, když najedete na prvek, spustil až po uplynutí určité doby? Tímto způsobem se můžete vyhnout nechtěnému spuštění efektu, pokud na něj uživatel náhodně najede myší.

Jednoduchý příklad setTimeout

Chcete-li demonstrovat, jak tato funkce funguje, doporučuji podívat se na následující ukázku, ve které se dvě sekundy po kliknutí na tlačítko objeví vyskakovací okno.

Zobrazit demo

Syntax

Dokumentace MDN poskytuje následující syntaxi pro setTimeout:

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

  • timeoutID – číselné ID, které lze použít v kombinaci s clearTimeout() k deaktivaci časovače;
  • func – funkce, která se má provést;
  • kód (v alternativní syntaxi) – řádek kódu, který se má provést;
  • zpoždění – doba zpoždění v milisekundách, po které bude funkce spuštěna. Výchozí hodnota je 0.
setTimeout vs window.setTimeout

Výše uvedená syntaxe používá window.setTimeout . Proč?

SetTimeout a window.setTimeout jsou ve skutečnosti prakticky stejné funkce. Jediný rozdíl je v tom, že ve druhém výrazu používáme metodu setTimeout jako vlastnost objektu globálního okna.

Osobně si myslím, že to dělá kód mnohem složitějším. Pokud jsme definovali alternativní metodaČasový limit JavaScriptu, který by mohl být nalezen a vrácen jako priorita, by pak narazil na ještě větší problémy.

V tomto tutoriálu se nechci zahrávat s objektem okna, ale obecně je na vás, abyste se rozhodli, jakou syntaxi použijete.

Příklady použití

Může to být název funkce:

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

Proměnná, která odkazuje na funkci:

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

Nebo anonymní funkce:

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

  • Takový kód je špatně srozumitelný, a proto bude obtížné jej modernizovat nebo ladit;
  • Zahrnuje použití metody eval(), která by mohla představovat potenciální zranitelnost;
  • Tato metoda je pomalejší než ostatní, protože vyžaduje spuštění interpretu JavaScriptu.

Upozorňujeme také, že k testování kódu používáme metodu upozornění na časový limit JavaScriptu.

Předání parametrů do setTimout

V první (a cross-browser) volbě předáme parametry funkci zpětného volání, provedené pomocí setTimeout.

V následujícím příkladu extrahujeme náhodný pozdrav z pole pozdravů a ​​předáme jej jako parametr funkci greet() , kterou provede setTimeout se zpožděním 1 sekundy:

function greet(zdraví)( console.log(zdraví); ) function getRandom(arr)( return arr; ) var greetings = ["Ahoj", "Bonjour", "Guten Tag"], randomGreeting = getRandom(zdraví); setTimeout(funkce())( pozdrav(náhodný pozdrav); ), 1000);

Zobrazit demo

Alternativní metoda

V syntaxi uvedené na začátku článku existuje další metoda, kterou můžete předat parametry funkci zpětného volání spuštěné časovým limitem JavaScriptu. Tato metoda znamená výstup všech parametrů po zpoždění.

Na základě předchozího příkladu dostaneme:

setTimeout(pozdrav, 1000, náhodný pozdrav);

Tato metoda nebude fungovat v IE 9 a nižších, kde jsou předané parametry považovány za nedefinované . Ale na MDN existuje speciální polyfill, který tento problém řeší.

Související problémy a „toto“

Kód spuštěný setTimeout běží odděleně od funkce, která jej volala. Z tohoto důvodu se setkáváme s určitými problémy, které lze vyřešit použitím klíčového slova this.

var person = ( firstName: "Jim", úvod: function())( console.log("Ahoj, jsem " + this.firstName); ) ); person.introduce(); // Výstupy: Ahoj, já " m Jim setTimeout(person.introduce, 50); // Výstupy: Ahoj, nejsem definován

Důvodem tohoto výstupu je, že v prvním příkladu to ukazuje na objekt osoba a ve druhém příkladu ukazuje na objekt globálního okna, který nemá vlastnost firstName.

Chcete-li se této nekonzistence zbavit, můžete použít několik metod:

Vynutit nastavení

To lze provést pomocí bind(), což je metoda, která vytvoří novou funkci, která po zavolání používá určitou hodnotu jako hodnotu klíče this. V našem případě objekt zadané osoby. To nám nakonec dává:

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

Poznámka: metoda vazby byla zavedena v ECMAScript 5, což znamená, že bude fungovat pouze v moderní prohlížeče. V jiných, když jej použijete, obdržíte chybu provádění JavaScriptu „chyba vypršení časového limitu funkce“.

Použít knihovnu

Mnoho knihoven obsahuje vestavěné funkce potřebné k vyřešení tohoto problému. Například metoda jQuery.proxy() . Vezme funkci a vrátí novou, která bude vždy používat konkrétní kontext. V našem případě bude kontext:

setTimeout($.proxy(person.introduce, person), 50);

Zobrazit demo

Deaktivace časovače

Vrácená hodnota setTimeout je číselné ID, které lze použít k deaktivaci časovače pomocí funkce clearTimeout():

var timer = setTimeout(myFunction, 3000); clearTimeout(časovač);

Podívejme se na to v akci. Pokud v následujícím příkladu kliknete na tlačítko „Spustit odpočítávání“, odpočítávání začne. Po jeho dokončení dostanou koťata své. Pokud však kliknete na tlačítko „Zastavit odpočítávání“, časovač časového limitu JavaScriptu se zastaví a resetuje.

Zobrazit příklad

Pojďme si to shrnout

setTimeout je asynchronní funkce, což znamená, že výsledné volání této funkce jde do fronty a bude provedeno až po dokončení všech ostatních akcí na zásobníku. Nemůže běžet současně s jinými funkcemi nebo samostatným vláknem.

Metoda setInterval() nabízená na rozhraních Window a Worker opakovaně volá funkci nebo provádí úryvek kódu s pevným časovým zpožděním mezi každým voláním.

Vrací ID intervalu, které jedinečně identifikuje interval, takže jej můžete později odstranit voláním clearInterval() . Tato metoda je definována mixinem WindowOrWorkerGlobalScope. Syntax = var intervalID rozsah .setInterval(, func, [zpoždění, arg1, ...]); Syntax = var intervalID rozsah arg2, func kód ); Parametry func Funkce, která se má provést po každé milisekundě zpoždění. Funkce nejsou předány žádné argumenty a neočekává se žádná návratová hodnota. ze stejných důvodů, kvůli kterým je použití eval() bezpečnostním rizikem. .setInterval( zpoždění Čas v milisekundách (tisíciny sekundy), který by měl časovač zpozdit mezi prováděním zadané funkce nebo kódu. Podrobnosti o povoleném rozsahu hodnot zpoždění viz níže.

arg1, ..., argN Nepovinné Další argumenty, které jsou předány funkci určené pomocí jakmile časovač vyprší. Poznámka: Předávání dalších argumentů do setInterval() v první syntaxi nefunguje

Internet Explorer

9 a dříve. Pokud chcete v tomto prohlížeči povolit tuto funkci, musíte použít polyfill (viz část).

Návratová hodnota

Vrácené intervalID je číselná, nenulová hodnota, která identifikuje časovač vytvořený voláním setInterval() ; tato hodnota může být předána ke zrušení časového limitu.

Může být užitečné si uvědomit, že setInterval() a setTimeout() sdílejí stejný fond ID a že clearInterval() a clearTimeout() lze technicky používat zaměnitelně. Pro srozumitelnost byste se však měli snažit je vždy porovnat, abyste předešli zmatkům při údržbě kódu.

Poznámka: Argument zpoždění je převeden na 32bitové celé číslo se znaménkem. To účinně omezuje zpoždění na 2147483647 ms, protože je v IDL specifikováno jako celé číslo se znaménkem.

Příklady Příklad 1: Základní syntaxe

Následující příklad ukazuje základní syntaxi setInterval() ".

Var intervalID = window.setInterval(myCallback, 500, "Parametr 1", "Parametr 2"); function myCallback(a, b) ( // Váš kód zde // Parametry jsou čistě volitelné. console.log(a); console.log(b); )

Příklad 2: Střídání dvou barev

Následující příklad volá funkci flashtext() jednou za sekundu, dokud nestisknete tlačítko Stop.

setInterval/clearInterval příklad var nIntervId;

function changeColor() ( nIntervId = setInterval(flashText, 1000); ) function flashText() ( var oElem = document.getElementById("my_box"); oElem.style.color = oElem.style.color == "red" ? " blue" : "red"; // oElem.style.color == "red" ? "blue" : "red" je ternární operátor. ) funkce stopTextColor() ( clearInterval(nIntervId); )

Ahoj světe

JavaScript Typewriter - MDN Příklad funkce Psací stroj (sSelector, nRate) ( funkce clean () ( clearInterval(nIntervId); bTyping = false; bStart = true; oCurrent = null; aSheets.length = nIdx = 0; ) funkce rolování (oSheet, nPos , bEraseAndStop) ( if (!oSheet.hasOwnProperty("části") || 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 od Mozilla Developer Network

[ Přehrát | Pauza | Ukončit]

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. V tincidunt tincidunt tincidunt.

JavaScript psací stroj

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 v ante tellus, v 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 tortor, 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 elita. Donec dignissim est in quam tempor consequat. Aliquam aliquam diam non felis convallis suscipit. Nulla facilisi. 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 tortor ultricies ac. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nec libero nunc. Nullam tortor nunc, elementum a consectetur et, ultrices eu orci. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque a nisl eu sem vehicula egestas.

Argumenty zpětného volání

Jak bylo uvedeno dříve, Internet Explorer verze 9 a nižší nepodporují předávání argumentů funkci zpětného volání v setTimeout() ani setInterval() . Následující kód specifický pro IE ukazuje metodu, jak toto omezení překonat. Chcete-li použít, jednoduše přidejte následující kód do horní části skriptu.

/*\ |*| |*| Polyfill specifický pro IE, který umožňuje průchod libovolných argumentů do |*| funkce zpětného volání javascriptových časovačů (standardní syntaxe HTML5)..setInterval |*| https://site/User:fusionchess |*| |*| Syntaxe: |*| var timeoutID = window.setTimeout(func, delay[, arg1, arg2, ...]); |*| var timeoutID = window.setTimeout(kód, zpoždění); |*| var intervalID = window.setInterval(func, delay[, arg1, arg2, ...]); |*| var intervalID = window.setInterval(kód, zpoždění); |*| \*/ if (document.all && !window.setTimeout.isPolyfill) ( var __nativeST__ = window.setTimeout; window.setTimeout = funkce (vCallback, nDelay /*, argumentToPass1, argumentToPass2 atd. */) ( var aArgs = Array .prototype.slice.call(arguments, 2) return __nativeST__(vCallback instanceof Function () ( vCallback.apply(null, aArgs); ) : vCallback, nDelay if (document.all && !window.setInterval.isPolyfill) ( var __nativeSI__ = window.setInterval; window.setInterval = funkce (vCallback, nDelay /*, argumentToPass1, argumentToPass2 atd. */) ( var aArgs = Array.prototype. slice.call(arguments, 2); return __nativeSI__(vCallback instanceof Funkce () ( vCallback.apply(null, aArgs); ) : vCallback, nDelay );

Další možností je použití anonymní funkce pro zavolání zpětného volání, i když toto řešení je o něco dražší. Příklad:

Var intervalID = setInterval(function() ( myFunc("jeden", "dva", "tři"); ), 1000);

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

Neaktivní karty Vyžaduje Gecko 5.0 (Firefox 5.0 / Thunderbird 5.0 / SeaMonkey 2.2)

Počínaje Gecko 5.0 (Firefox 5.0 / Thunderbird 5.0 / SeaMonkey 2.2) jsou intervaly na neaktivních kartách omezeny tak, aby se spouštěly maximálně jednou za sekundu.

"Tento" problém

Když předáte metodu setInterval() nebo jiné funkci, je vyvolána s nesprávnou hodnotou. Tento problém je podrobně vysvětlen v referenci JavaScriptu.

Vysvětlení

MyArray = ["nula", "jedna", "dva"]; myArray.myMethod = function (sProperty) ( alert(arguments.length > 0 ? this : this); ); myArray.myMethod(); // vypíše "nula,jedna,dva" myArray.myMethod(1); // vypíše "jeden" setTimeout(myArray.myMethod, 1000); // vytiskne "" po 1 sekundě setTimeout(myArray.myMethod, 1500, "1"); // po 1,5 sekundách vypíše "undefined" // předání objektu "this" pomocí .call nebude fungovat // protože to změní hodnotu tohoto uvnitř samotného setTimeout // zatímco my chceme změnit hodnotu tohoto uvnitř myArray .myMethod // ve skutečnosti to bude chyba, protože kód setTimeout očekává, že to bude objekt okna: setTimeout.call(myArray, myArray.myMethod, 2000 // error: "NS_ERROR_XPC_BAD_OP_ON_WN_PROTO: Neplatná operace na objektu WrappedNative prototype); setTimeout.call(myArray, myArray.myMethod, 2500, 2);

Jak vidíte, neexistují žádné způsoby, jak předat tento objekt funkci zpětného volání ve starším JavaScriptu.

Možné řešení

Možným způsobem, jak vyřešit „tento“ problém, je nahradit dvě nativní globální funkce setTimeout() nebo setInterval() dvěma nepůvodní takové, které umožňují jejich vyvolání prostřednictvím metody Function.prototype.call. Následující příklad ukazuje možnou náhradu:

// Povolí průchod „tohoto“ objektu přes časovače JavaScriptu var __nativeST__ = window.setTimeout, __nativeSI__ = window.setInterval; window.setTimeout = funkce (vCallback, nDelay /*, argumentToPass1, argumentToPass2 atd. */) ( var oThis = this, aArgs = Array.prototype.slice.call(arguments, 2); return __nativeST__(vCallback instanceof Funkce ? funkce () ( vCallback.apply(oThis, aArgs); ) : vCallback, nDelay ); window.setInterval = function (vCallback, nDelay /*, argumentToPass1, argumentToPass2 atd. */) ( var oThis = this, aArgs = Array.prototype.slice.call(arguments, 2); return __nativeSI__(vCallback instanceof Funkce ? funkce () ( vCallback.apply(oThis, aArgs); ) : vCallback, nDelay );

Tyto dvě náhrady také umožňují standardní přechod libovolných argumentů do funkcí zpětného volání časovačů v IE standardu HTML5. Lze je tedy použít jako nestandardně vyhovující polyfilly také. Viz a vyhovující normě polyfill.

Test nových funkcí:

MyArray = ["nula", "jedna", "dva"]; myArray.myMethod = function (sProperty) ( alert(arguments.length > 0 ? this : this); ); setTimeout(upozornění, 1500, "Ahoj světe!"); // standardní použití setTimeout a setInterval je zachováno, ale... setTimeout.call(myArray, myArray.myMethod, 2000); // po 2 sekundách vypíše "nula,jedna,dva" setTimeout.call(myArray, myArray.myMethod, 2500, 2); // vytiskne "dva" po 2,5 sekundách

Pro složitější, ale stále modulární verzi ( Démon) viz Správa démonů JavaScriptu . Tato složitější verze není nic jiného než velká a škálovatelná sbírka metod pro Démon konstruktér. Nicméně, Démon konstruktor sám o sobě není nic jiného než klon MiniDaemon s přidanou podporou init a nastartovat funkce deklarované během vytváření instance démon. Takže MiniDaemon framework zůstává doporučeným způsobem pro jednoduché animace, protože Démon bez jeho sbírky metod je v podstatě jeho klonem.

minidaemon.js /*\ |*| |*| :: MiniDaemon:: |*| |*| Revize #2 – 26. září 2014.setInterval |*| https://site/User:fusionchess |*| https://github.com/madmurphy/minidaemon.js |*| |*| Tento rámec je vydán pod licencí GNU Lesser General Public License, verze 3 nebo novější. |*| http://www.gnu.org/licenses/lgpl-3.0.html |*| \*/ funkce MiniDaemon (oOwner, fTask, nRate, nLen) ( if (!(this && this instanceof 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 = null; MiniDaemon.prototype.rate = 100; MiniDaemon.prototype.length = Infinity;< 1: this.INDEX + 1 >tato.délka; ); 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 = funkce (bReverse) ( var bBackw = Boolean(bReverse); if (this.BACKW === bBackw && (this.isAtEnd() || !this.PAUSED)) ( return; ) this.BACKW = bBackw toto.PAUSED = false.synchronize();

MiniDaemon předává argumenty funkci zpětného volání. Pokud na něm chcete pracovat s prohlížeči, které tuto funkci nativně nepodporují, použijte jednu z výše navržených metod.

Syntax

var myDaemon = nový MiniDaemon( tentoObjekt, zpětné volání[ , hodnotit [, délka]]);

Popis Poznámky k použití

Funkce setInterval() se běžně používá k nastavení zpoždění pro funkce, které se spouštějí znovu a znovu, jako jsou animace. Interval můžete zrušit pomocí WindowOrWorkerGlobalScope.clearInterval() .

Pokud si přejete zavolat svou funkci jednou po zadaném zpoždění použijte .

Omezení zpoždění

Je možné, aby byly intervaly vnořeny; to znamená, že zpětné volání pro setInterval() může zase volat setInterval() ke spuštění dalšího intervalu, i když ten první stále běží. Aby se zmírnil potenciální dopad, který to může mít na Jakmile jsou intervaly vnořeny za hranicí pěti úrovní, prohlížeč automaticky vynutí minimální hodnotu 4 ms pro interval Pokusy zadat hodnotu menší než 4 ms v hluboce vnořených voláních setInterval() budou připnuty na 4 ms.

Prohlížeče mohou za určitých okolností vynutit ještě přísnější minimální hodnoty intervalu, i když by to nemělo být běžné. Všimněte si také, že skutečná doba, která uplyne mezi voláními zpětného volání, může být delší než dané zpoždění; příklady najdete v části Důvody pro zpoždění delší, než je uvedeno v WindowOrWorkerGlobalScope.setTimeout().

Ujistěte se, že doba provádění je kratší než intervalová frekvence

Pokud existuje možnost, že provedení vaší logiky bude trvat déle, než je časový interval, doporučuje se rekurzivně zavolat pojmenovanou funkci pomocí setTimeout() . Pokud například použijete metodu setInterval() k dotazování vzdáleného serveru každých 5 sekund, latence sítě, neodpovídající server a řada dalších problémů může zabránit dokončení požadavku ve stanoveném čase. Proto se můžete ocitnout ve frontě s požadavky XHR, které se nemusí nutně vrátit v pořádku.

Je nesmírně důležité pochopit, jak fungují časovače JavaScriptu. Jejich chování často neodpovídá našemu intuitivnímu chápání multithreadingu, a to je způsobeno tím, že ve skutečnosti jsou prováděny v jediném vláknu. Podívejme se na čtyři funkce, pomocí kterých můžeme spravovat časovače:

  • var id = setTimeout(fn, zpoždění); - Vytvoří jednoduchý časovač, který zavolá danou funkci po daném zpoždění. Funkce vrací jedinečné ID, pomocí kterého lze časovač pozastavit.
  • var id = setInterval(fn, zpoždění); - Podobné jako setTimeout, ale nepřetržitě volá funkci v daném intervalu (až do zastavení).
  • clearInterval(id);, clearTimeout(id); - Přijímá ID časovače (vrácené jednou z funkcí popsaných výše) a zastaví provádění zpětného volání"a.
Hlavní myšlenkou, kterou je třeba vzít v úvahu, je, že není zaručena přesnost doby zpoždění časovače. Za prvé, prohlížeč provádí všechny asynchronní události JavaScriptu v jednom vlákně (jako jsou kliknutí myší nebo časovače) a pouze v době, kdy je na řadě tato událost. Nejlépe to ukazuje následující diagram:

Tento obrázek obsahuje poměrně hodně informací, ale jejich pochopení vám poskytne hlubší pochopení toho, jak funguje asynchronie JavaScriptu. Tento graf představuje čas vertikálně v milisekundách, modré bloky ukazují bloky kódu JavaScript, které byly provedeny. Například první blok je proveden v průměru za 18 ms, kliknutí myší blokuje provedení na přibližně 11 ms atd.

JavaScript může spustit pouze jeden kus kódu (kvůli jednovláknové povaze provádění), z nichž každý blokuje provádění jiných asynchronních událostí. To znamená, že když dojde k asynchronní události (například kliknutí myší, volání časovače nebo dokončení požadavku XMLHttp), je přidána do fronty a spuštěna později (implementace se samozřejmě liší podle prohlížeče, ale domluvme se na nazývat to „fronta“).

Pro začátek si představme, že dva časovače začínají uvnitř bloku JavaScriptu: setTimeout se zpožděním 10 ms a setInterval se stejným zpožděním. Podle toho, kdy se časovač spustí, se spustí v okamžiku, kdy ještě nemáme dokončený první blok kódu. Všimněte si však, že se nespustí okamžitě (to není možné kvůli jedinému závitu). Místo toho je odložená funkce zařazena do fronty a spuštěna v příštím dostupném okamžiku.

Během provádění prvního bloku JavaScriptu také dojde ke kliknutí myší. Obslužnou rutinu této asynchronní události (a ta je asynchronní, protože ji nemůžeme předvídat) nelze v tuto chvíli provést okamžitě, takže také skončí ve frontě, jako časovač.

Po provedení prvního bloku kódu JavaScript se prohlížeč zeptá: „Co čeká na spuštění?“ V v tomto případě Ovladač kliknutí myší a časovač jsou ve stavu čekající na vyřízení. Prohlížeč vybere jeden z nich (obslužný program kliknutí) a spustí jej. Časovač bude čekat na další dostupnou část času ve frontě provádění.

Všimněte si, že zatímco se provádí obslužná rutina kliknutí myší, spustí se první intervalové zpětné volání. Stejně jako zpětné volání časovače bude zařazeno do fronty. Pamatujte však, že když se interval znovu spustí (během zpětného volání časovače), bude odstraněn z fronty. Pokud by všechna zpětná volání v intervalu byla zařazena do fronty během provádění velkého kusu kódu, vedlo by to ke spoustě funkcí čekajících na zavolání, aniž by mezi nimi došlo k žádné prodlevě při dokončení provádění. Místo toho mají prohlížeče tendenci čekat, dokud nebudou žádné další funkce vlevo ve frontě před přidáním dalšího do fronty.

Můžeme tedy pozorovat případ, kdy se třetí odpálení intervalového zpětného volání shoduje s okamžikem, kdy je již provedeno. To ilustruje důležitý bod: intervaly se nestarají o to, co právě běží, budou přidány do fronty bez ohledu na prodlevu mezi spuštěními.

Nakonec, po dokončení druhého intervalového zpětného volání, uvidíme, že už nezbývá nic, co by JavaScriptový engine mohl spustit. To znamená, že prohlížeč opět čeká na nové asynchronní události. K tomu dojde při značce 50 ms, kdy bude opět fungovat intervalové zpětné volání. V tuto chvíli už to nebude nic blokovat, takže to bude fungovat okamžitě.

Podívejme se na příklad, který pěkně ilustruje rozdíl mezi setTimeout a setInterval.
setTimeout(function())( /* Nějaký dlouhý blok kódu... */ setTimeout(arguments.callee, 10); ), 10);
Tyto dvě možnosti jsou na první pohled rovnocenné, ale ve skutečnosti nejsou. Kód používající setTimeout bude mít vždy zpoždění alespoň 10 ms po předchozím volání (může to být více, ale nikdy méně), zatímco kód používající setInterval bude mít tendenci volat každých 10 ms, bez ohledu na to, kdy došlo k předchozímu volání.

Pojďme si shrnout vše, co bylo řečeno výše:
- JavaScriptové motory používají jednovláknové prostředí, převádějící asynchronní události na frontu čekající na spuštění,
- Funkce setTimeout a setInterval se v asynchronním kódu provádějí zásadně odlišně,
- Pokud nelze časovač spustit v momentálně, bude zpožděn do dalšího bodu provedení (který bude delší než požadované zpoždění),
- Intervaly (setInterval) mohou být provedeny jeden po druhém bez zpoždění, pokud jejich provedení trvá déle než zadaná prodleva.

To vše je extrémně důležité informace pro rozvoj. Znalost toho, jak funguje JavaScript engine, zvláště se spoustou asynchronních událostí (což se často stává), pokládá skvělý základ pro vytváření pokročilých aplikací.

Při programování ve skriptovacích jazycích je periodicky potřeba vytvořit pauzu - na chvíli pozastavit provádění programu a pak pokračovat v práci. Například ve skriptech VBS a PHP jsou možné následující metody:

VBS: wscript.sleep 1500 (zastavení na 1,5 sekundy)

PHP: spánek(10); (zastavte se na 10 sekund)

Během takových přestávek běží runtime systém (PHP nebo VBS) nedělá nic. Vývojář, který se snaží intuitivně použít něco podobného v Javascriptu, bude nemile překvapen. Typická chyba při pokusu o vytvoření pauzy v Javascriptu vypadá takto:

Funkce badtest() ( for (var i=1; i< 10; i++) { window.setTimeout("document.getElementById("test1").value += " + i, 900) } }

Myslíte si, že když během cyklu přijde řada na kreslení dalšího čísla, váš setTimeout poctivě zastaví Javascript v práci, počká 0,9 sekundy, přidá požadované číslo na konec vstupního pole a pak pokračuje v práci. Ale ve skutečnosti tomu tak není: setInterval a setTimeout v Javascriptu pouze zdržují provedení akce (nebo funkce) uvedené v závorkách. V našem příkladu se stane následující:

  • i = 1;
  • zpoždění přidání čísla "1" do vstupního pole o 0,9 sekundy;
  • Ihned po nastavení tohoto problému cyklus pokračuje: i=2;
  • zpoždění přidání čísla "2" do vstupního pole o 0,9 sekundy;
  • Okamžitě znamená například 1 ms (tedy neúměrně málo ve srovnání s 900 ms): smyčka vykoná svou práci téměř okamžitě a vytvoří několik odložených úloh ze stejného okamžiku. To znamená, že všechny nevyřízené úkoly „kreslení“ budou dokončeny téměř ve stejnou dobu, bez pauz mezi přidáváním nových čísel. Cyklus začíná; vše zamrzne na 0,9 s; a shirr - všechna čísla jsou střílena v řadě za sebou.

    Jak v takovém případě správně použít setTimeout? Je to složité. Budete muset zavolat funkci rekurzivně(v rámci funkce stejná funkce), a aby tento proces nebyl nekonečný, nastavte podmínku zastavení (například velikost čísla, které se má vytisknout):

    Funkce welltest() (pokud (i< 9) { document.getElementById("test2").value += ++i window.setTimeout("welltest()", 400) } }

    A proměnná i bude muset být inicializována mimo funkci - například takto:

    Nyní vše funguje jak má (zkrátili jsme dobu zpoždění z 0,9 s na 0,4 s). Ale pro takové úkoly je logičtější použít setInterval spíše než setTimeout (i když to bude vyžadovat dvě funkce):

    Funkce besttest() ( window.i = 0 window.timer1 = window.setInterval("draw()", 400) ) funkce draw() ( document.getElementById("test3").value += ++i if (i >= 9) clearInterval(window.timer1) )

    Zvláštností metody Javascirpt setInterval je, že neprochází „sama od sebe“ a musí být zastavena speciální metodou clearInterval. A aby bylo jasné, co přesně zastavit, je úkolu pro odloženou akci přiřazen speciální identifikátor - časovač: window.timer1 = window.setInterval(...) .

    Úkolům vytvořeným metodou setTimeout lze také přiřadit identifikátory. Všechna ID časovače musí být navzájem odlišná (unikátní v aktuálním okně prohlížeče). V okně pak můžete vytvořit několik různých úloh, které využívají odložené akce, a tyto úlohy se budou provádět paralelně (tak nějak současně, pokud má počítač dostatek prostředků), což je v PHP nebo VBS v podstatě nemožné.

    Zde je příklad stránky s několika Javascriptovými časovači spuštěnými současně: setinterval.htm (funkce JavaScriptu v souboru setinterval.js). Všechny časovače stránky (kromě menu) lze zastavit klávesou Esc. Všechny příklady časovačů jsou založeny na „přirozeném“ (a nikoli abstraktním i++) odpočítávání – času nebo vzdálenosti. Všechny „hodiny“ jsou speciálně desynchronizované (pro přehlednost). Časovače závislé na vzdálenosti se používají v „indikátoru“ a v rozbalovací („rozbalovací“) nabídce.

    Rozbalovací nabídka

    Naše posuvné menu je ve skutečnosti posuvné (zpod „záhlaví“): mezi prvky jsou speciálně ponechány mezery, takže můžete vidět, jak se vysouvá. Nečekaně se ukázalo, že stejně hladký výstup u různě dlouhých seznamů nedokážeme udělat – pravděpodobně kvůli nízkému výkonu počítače (AMD Athlon 999 MHz).

    Je zcela zřejmé, že pro krásu a harmonii je nutné, aby se seznamy různých položek menu objevovaly současně. To znamená, že delší seznamy by měly vypadnout s více vysoká rychlost, kratší - při nižší rychlosti. Zdá se, že by to mohlo být implementováno takto:

  • Celkový čas „odjezdu“ nastavíme např. na 200 ms.
  • Pokud má rozbalovací seznam výšku 20 px, je zřejmé, že jej můžeme posunout o jeden pixel dolů za 10 ms interval – a pak za 200 ms vyjede celý seznam.
  • Pokud je rozevírací seznam vysoký 40 pixelů, abychom se vešli za stejnou dobu, musíme jej každých 5 ms posunout o jeden pixel dolů.
  • Podle této logiky, pokud je rozevírací seznam vysoký 200 pixelů, měli bychom jej posunout o jeden pixel dolů každou 1 ms. Taková rychlost ale na našem počítači nefunguje – prohlížeč prostě nestihne nakreslit novou pozici seznamu za jednu milisekundu. Ano. Javascript zvládá počítat (co se tam má počítat?), ale prohlížeč (Firefox) nestihne zobrazit. Typická situace pro web.

    Proto je možné víceméně vyrovnat čas odchodu z menu pouze pomocí berliček a stále není jasné, jak to bude fungovat déle rychlý počítač. Ale měli bychom počítat s tím nejpomalejším, že? Algoritmus (bez zohlednění rychlosti počítače) vypadá asi takto:

  • Nastavte celkový čas pro kontrolu seznamu: čas = 224 (ms).
  • Nastavíme minimální čas pro jeden interval v cyklu: zpoždění = 3 (ms).
  • Nastavte minimální krok pro přesun seznamu: offset = 1 (px).
  • To vše měníme v závislosti na výšce seznamu: 1) zvyšujeme čas zpoždění (intervalu) nepřímo úměrně výšce a přímo úměrně celkovému času času (při výšce 224 je koeficient 1); 2) pokud je výška větší než 40 px, zvyšte minimální krok v poměru k výšce. Konstanta "40" byla získána experimentálně pro nejpomalejší počítač. Testy na počítači Pentium 4 CPU 2,53GHz odhalily úplně stejné číslo – 40. V opačném případě se časovače vyřadí z provozu, seznamy vyjdou z kroku.
  • Nyní seznamy víceméně vycházejí. Na víceméně podobnou dobu. Na stránce setinterval.htm.

    A tady přichází Bruce:

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

    Samotná funkce, která vytlačí vnořené seznamy z nabídky, je, jak vidíme, velmi jednoduchá. Vše, co zbývá, je spustit jej s něčím jako je tento řádek:

    Ts.timer1 = setInterval(function())(slide_do(ts, maxtop, offset)), delay)

    Než začnete, stačí vypočítat všechny tyto maxtop a offset a také umístit seznam na pozici mintop. To je to, co dělá „předběžná“ funkce slide() o 40 řádcích. A to vše dohromady – v souboru setinterval.js. Ano, a bez přiloženého souboru stylů tato kravina vůbec nebude fungovat

    • Z:
    • Registrováno: 2014.07.08
    • Příspěvky: 3 896
    • Líbí se: 497
    Téma: SetTimeOut a SetInterval, co je lepší použít v JavaScriptu?

    Funkce setInterval je navržena tak, aby spouštěla ​​kód vícekrát v pravidelných intervalech. Má však řadu nevýhod, především odlišné chování v různé prohlížeče.

    První rozdíl je rozdíl v čase, kdy je časovač nastaven pro další spuštění. Vytvořme si malý test: změříme dobu, která uplynula od začátku předchozího běhu a od jeho konce.

    var d1 = new Date(), d2 = new Date(); setInterval(function() ( var d = new Date(); document.body.innerHTML += (d - d1) + " " + (d - d2) + "
    "; // Na začátek funkce vložte značku d1 = new Date(); while (new Date() - d1< 200); // ничего не делаем 200 миллисекунд // И в конце функции d2 = new Date(); }, 1000);

    Výstup bude informativní od druhého řádku.

    Ve Firefoxu, Opeře, Safari a Chrome bude situace podobná: první číslo bude přibližně rovné 1000, druhé - o 200 méně. Rozdíl bude pouze v rozložení hodnot. Nejmenší rozdíl je v Chrome a Opeře.

    2 Odpověď od PunBB (upraveno PunBB 2017.06.08 16:45)
    • Od: Moskva, Sovkhoznay 3, apt. 98
    • Registrováno: 2014.07.08
    • Příspěvky: 3 896
    • Líbí se: 497

    Dalším rozdílem, který je méně patrný a obtížněji reprodukovatelný, ale někdy může způsobit velké potíže, je odolnost vůči změnám systémového času. Pokud spustíte následující test

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

    A po spuštění nastavte systémový čas o minutu zpět a poté na Prohlížeče Firefox a Safari se změna čísla pozastaví a po minutě začne znovu. Ruční překlad systémového času je samozřejmě extrémně vzácná situace, ale mnoho systémů je nakonfigurováno tak, aby automaticky synchronizovaly čas se servery na internetu, takže v některých situacích nelze tento faktor zanedbat.

    Další drobnou nevýhodou funkce setInterval je, že abyste mohli zastavit její činnost, musíte si někde zapamatovat její identifikátor, což se ne vždy hodí.

    3 Odpověď od PunBB
    • Od: Moskva, Sovkhoznay 3, apt. 98
    • Registrováno: 2014.07.08
    • Příspěvky: 3 896
    • Líbí se: 497
    Re: SetTimeOut a SetInterval, co je lepší použít v JavaScriptu?

    Chcete-li se zbavit uvedených nevýhod setInterval, můžete použít více setTimeout.

    Důležitou alternativou k setInterval je rekurzivní setTimeout:

    /** místo: var timerId = setInterval(function() ( alert("tick"); ), 2000); */ var timerId = setTimeout(funkce tick() ( alert("tick"); timerId = setTimeout(tick, 2000); ), 2000);

    Ve výše uvedeném kódu je další spuštění naplánováno ihned po dokončení předchozího.

    Rekurzivní setTimeout je flexibilnější metoda časování než setInterval, protože čas do dalšího spuštění lze naplánovat odlišně v závislosti na výsledcích toho aktuálního.

    Máme například službu, která každých 5 sekund dotazuje server na nová data. Pokud je server přetížený, můžete zvýšit interval dotazování na 10, 20, 60 sekund... A pak jej vrátit zpět, až se vše vrátí do normálu.

    Pokud pravidelně spouštíme úlohy náročné na CPU, pak můžeme odhadnout čas strávený jejich prováděním a naplánovat další spuštění dříve nebo později.

    4 Odpověď od PunBB
    • Od: Moskva, Sovkhoznay 3, apt. 98
    • Registrováno: 2014.07.08
    • Příspěvky: 3 896
    • Líbí se: 497
    Re: SetTimeOut a SetInterval, co je lepší použít v JavaScriptu?

    Rekurzivní setTimeout zaručuje pauzu mezi hovory, setInterval nikoli.

    Porovnejme oba kódy. První používá setInterval:

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

    Druhý používá rekurzivní setTimeout:

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

    S setInterval se interní časovač spustí přesně každých 100 ms a zavolá funkci func(i):

    Skutečná pauza mezi voláními funkce setInterval je menší, než je uvedeno v kódu!

    Je to přirozené, protože provozní doba funkce není nijak zohledněna, část intervalu „sežere“.

    Je také možné, že se funkce func ukázala být složitější, než jsme očekávali, a její provedení trvalo déle než 100 ms.

    V tomto případě tlumočník počká na dokončení funkce, poté zkontroluje časovač a pokud čas pro volání setInterval již nastal (nebo uplynul), další volání proběhne okamžitě.

    Pokud funkce běží déle než pauza setInterval, pak volání proběhnou bez jakéhokoli přerušení.

    5 Odpověď od sempai
    • Od: Jeruzalém
    • Registrováno: 2015.06.02
    • Příspěvky: 958
    • Líbí se: 274
    Re: SetTimeOut a SetInterval, co je lepší použít v JavaScriptu?

    Vše závisí na aktuálním úkolu. Zpočátku se SetTimeOut používá k jednorázovému spuštění časovače a SetInterval se používá ke spuštění smyčky. Ale obě funkce lze použít ke spouštění skriptů cyklicky, pokud je například spouštíte rekurzivně v Funkce SetTimeOut, pak bude fungovat téměř stejně jako SetInterval.

    Nevýhodou SetIntervalu v tuto chvíli je, že nezohledňuje dobu provádění samotného skriptu (funkce) a pokud jej například používáte pro těžké dotazy, pak se doba intervalu výrazně zkrátí a se může v různých prohlížečích lišit.

    Ale znovu, pokud je funkce nebo požadavek minimalizován, pak koncový uživatel pravděpodobně nepocítí rozdíl.
    Proto, co použít, je na každém, aby se rozhodl sám.

    © 2024 ermake.ru -- O opravě PC - Informační portál