Інтернет Windows Android

Javascript заборонити одночасний запуск декількох таймерів setinterval. Таймери в Javascript (setInterval, setTimeout)

JavaScript timeout представляє собою нативну javascript-функцію, яка виконує фрагмент коду після встановленої тривалості затримки (в мілісекундах). Це може стати в нагоді, коли потрібно вивести спливаюче віконце після того, як користувач провів деякий час на вашій сторінці. Або потрібно, щоб ефект при наведенні курсору на елемент запускався лише через якийсь час. Таким чином, можна уникнути ненавмисного запуску ефекту, якщо користувач навів курсор випадково.

Простий приклад setTimeout

Щоб продемонструвати дію цієї функції, пропоную поглянути на наступне демо, в якому спливаюче вікно з'являється через дві секунди після кліка по кнопці.

Подивитися демо

синтаксис

У документації MDN приведений наступний синтаксис для setTimeout:

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

  • timeoutID - числовий id, який можна використовувати в поєднанні з clearTimeout () для відключення таймера;
  • func - функція, яка повинна бути виконана;
  • code ( в альтернативному синтаксисі) - рядок коду, яку потрібно виконати;
  • delay - тривалість затримки в мілісекундах, після якої буде запущена функція. За умовчанням встановлено значення 0.

setTimeout vs window.setTimeout

У наведеному вище синтаксисі використовується window.setTimeout. Чому?

Насправді, setTimeout і window.setTimeout - це практично одна і та ж функція. Єдина різниця полягає в тому, що в другому вираженні ми використовуємо метод setTimeout як властивість глобального об'єкта window.

Особисто я вважаю, що це лише сильно ускладнює код. Якби ми визначили альтернативний метод JavaScript timeout, який може бути знайдений і повернутий в пріоритетному порядку, то зіткнулися б з ще більшими проблемами.

У цьому посібнику я не хочу зв'язуватися з об'єктом window, але в цілому, ви самі вирішуєте, який синтаксис варто використовувати.

приклади використання

Це може бути назва функції:

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

Змінна, яка звертається до функції:

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

Або ж анонімна функція:

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

  • Такий код погано сприймається, а, отже, його складно буде модернізувати або налагодити;
  • Він передбачає використання методу eval (), який може стати потенційною вразливістю;
  • Цей метод працює повільніше інших, так як йому потрібно запускати JavaScript-інтерпретатор.

Також зверніть увагу, що для тестування коду ми використовуємо метод alert для JavaScript timeout.

Передаємо параметри в setTimout

У першому ( до того ж, кросбраузерності) Варіанті ми передаємо параметри в callback-функцію, виконувану за допомогою setTimeout.

У наступному прикладі ми виділяємо випадкове привітання з масиву greetings і передаємо його в якості параметра функції greet (), яка виконується setTimeout з затримкою в 1 секунду:

function greet (greeting) (console.log (greeting);) function getRandom (arr) (return arr;) var greetings = [ "Hello", "Bonjour", "Guten Tag"], randomGreeting = getRandom (greetings); setTimeout (function () (greet (randomGreeting);) 1000);

Подивитися демо

альтернативний метод

У синтаксисі, наведеному на початку статті, існує ще один метод, за допомогою якого можна передати параметри в callback-функцію, виконувану JavaScript timeout. Даний метод має на увазі під собою виведення всіх параметрів, таких після затримки.

Спираючись на попередній приклад, ми отримуємо:

setTimeout (greet, 1000, randomGreeting);

Цей метод не буде працювати в IE 9 і нижче, де передаються параметри розцінюються як undefined. Але для вирішення цієї проблеми на MDN є спеціальний поліфілл.

Супутні проблеми і "this"

Код, що виконується setTimeout, запускається окремо від функції, яку він був викликаний. Через це ми стикаємося з певними проблемами, як рішення яких можна використовувати ключове слово this.

var person = (firstName: "Jim", introduce: function () (console.log ( "Hi, I" m "+ this.firstName);)); person.introduce (); // Outputs: Hi, I" m Jim setTimeout (person.introduce, 50); // Outputs: Hi, I "m undefined

Причина такого висновку криється в тому, що в першому прикладі this веде до об'єкта person, а в другому прикладі - вказує на глобальний об'єкт window, у якого відсутня властивість firstName.

Щоб позбутися від цієї нестиковки, можна скористатися кількома методами:

Примусово встановити значення this

Це можна зробити за допомогою bind () - методу, який створює нову функцію, яка при виклику в якості значення ключа this використовує певне значення. У нашому випадку - зазначений об'єкт person. Це в результаті дає нам:

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

Примітка: метод bind був представлений в ECMAScript 5, а значить, що він буде працювати тільки в сучасних браузерах. В інших при його застосуванні ви отримаєте помилку виконання JavaScript «function timeout error».

використовувати бібліотеку

Багато бібліотек включають в себе вбудовані функції, необхідні для вирішення проблеми з this. Наприклад, метод jQuery.proxy (). Він бере функцію і повертає нову, в якій завжди буде використовувати певний контекст. У нашому випадку, контекстом буде:

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

Подивитися демо

відключення таймера

Повернене значення setTimeout є числовий id, який можна використовувати для відключення таймера за допомогою функції clearTimeout ():

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

Давайте подивимося на неї в дії. У наступному прикладі, якщо клікнути на кнопку « Start countdown», Почнеться зворотний відлік. Після того, як він завершиться, кошенята отримають своє. Але якщо натиснути кнопку « Stop countdown», Таймер JavaScript timeout буде зупинений і скинутий.

Подивитися приклад

Підведемо підсумки

setTimeout - асинхронна функція, а це значить, що отримане звернення до цієї функції потрапляє в чергу, і буде виконано тільки після того, як завершаться всі інші дії в стеці. Вона не може працювати одночасно з іншими функціями або окремим потоком.

The setInterval () method, offered on the Window and Worker interfaces, repeatedly calls a function or executes a code snippet, with a fixed time delay between each call. It returns an interval ID which uniquely identifies the interval, so you can remove it later by calling clearInterval (). This method is defined by the WindowOrWorkerGlobalScope mixin.

Syntax

var intervalID = scope.setInterval ( func, delay, [arg1, arg2, ...]); var intervalID = scope.setInterval ( code, delay);

Parameters

func A function to be executed every delay milliseconds. The function is not passed any arguments, and no return value is expected. code An optional syntax allows you to include a string instead of a function, which is compiled and executed every delay milliseconds. This syntax is not recommended for the same reasons that make using eval () a security risk. delay The time, in milliseconds (thousandths of a second), the timer should delay in between executions of the specified function or code. See below for details on the permitted range of delay values. arg1, ..., argN Optional Additional arguments which are passed through to the function specified by func once the timer expires.

Note: Passing additional arguments to setInterval () in the first syntax does not work in Internet Explorer 9 and earlier. If you want to enable this functionality on that browser, you must use a polyfill (see the section).

Return value

The returned intervalID is a numeric, non-zero value which identifies the timer created by the call to setInterval (); this value can be passed to to cancel the timeout.

It may be helpful to be aware that setInterval () and setTimeout () share the same pool of IDs, and that clearInterval () and clearTimeout () can technically be used interchangeably. For clarity, however, you should try to always match them to avoid confusion when maintaining your code.

Note: The delay argument is converted to a signed 32-bit integer. This effectively limits delay to 2147483647 ms, since it "s specified as a signed integer in the IDL.

Examples

Example 1: Basic syntax

The following example demonstrates setInterval () "s basic syntax.

Var intervalID = window.setInterval (myCallback, 500, "Parameter 1", "Parameter 2"); function myCallback (a, b) (// Your code here // Parameters are purely optional. console.log (a); console.log (b);)

Example 2: Alternating two colors

The following example calls the flashtext () function once a second until the Stop button is pressed.

setInterval / clearInterval example

Hello World



Example 3: Typewriter simulation

The following example simulates typewriter by first clearing and then slowly typing content into the NodeList that matches a specified group of selectors.

JavaScript Typewriter - MDN Example

CopyLeft 2012 by Mozilla Developer Network

[ Play | Pause | Terminate ]

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.

JavaScript Typewriter

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



Callback arguments

As previously discussed, Internet Explorer versions 9 and below do not support the passing of arguments to the callback function in either setTimeout () or setInterval (). The following IE-specific code demonstrates a method for overcoming this limitation. To use, simply add the following code to the top of your script.

/ * \ | * | | * | IE-specific polyfill that enables the passage of arbitrary arguments to the | * | callback functions of javascript timers (HTML5 standard syntax) .. setInterval | * | https: // сайт / User: fusionchess | * | | * | Syntax: | * | var timeoutID = window.setTimeout (func, delay [, arg1, arg2, ...]); | * | var timeoutID = window.setTimeout (code, delay); | * | var intervalID = window.setInterval (func, delay [, arg1, arg2, ...]); | * | var intervalID = window.setInterval (code, delay); | * | \ * / If (document.all &&! Window.setTimeout.isPolyfill) (var __nativeST__ = window.setTimeout; window.setTimeout = function (vCallback, nDelay / *, argumentToPass1, argumentToPass2, etc. * /) (var aArgs = Array .prototype.slice.call (arguments, 2); return __nativeST __ (vCallback instanceof Function? function () (vCallback.apply (null, aArgs);): vCallback, nDelay);); window.setTimeout.isPolyfill = true;) if (document.all &&! window.setInterval.isPolyfill) (var __nativeSI__ = window.setInterval; window.setInterval = function (vCallback, nDelay / *, argumentToPass1, argumentToPass2, etc. * /) (var aArgs = Array.prototype. slice.call (arguments, 2); return __nativeSI __ (vCallback instanceof Function? function () (vCallback.apply (null, aArgs);): vCallback, nDelay);); window.setInterval.isPolyfill = true;)

Another possibility is to use an anonymous function to call your callback, although this solution is a bit more expensive. Example:

Var intervalID = setInterval (function () (myFunc ( "one", "two", "three");) 1000); var intervalID = setInterval (function (arg1) () .bind (undefined, 10) 1000);

Inactive tabs

Requires Gecko 5.0 (Firefox 5.0 / Thunderbird 5.0 / SeaMonkey 2.2)

Starting in Gecko 5.0 (Firefox 5.0 / Thunderbird 5.0 / SeaMonkey 2.2), intervals are clamped to fire no more often than once per second in inactive tabs.

The "this" problem

When you pass a method to setInterval () or any other function, it is invoked with the wrong this value. This problem is explained in detail in the JavaScript reference.

Explanation

Code executed by setInterval () runs in a separate execution context than the function from which it was called. As a consequence, the this keyword for the called function is set to the window (or global) object, it is not the same as the this value for the function that called setTimeout. See the following example (which uses setTimeout () instead of setInterval () - the problem, in fact, is the same for both timers):

MyArray = [ "zero", "one", "two"]; myArray.myMethod = function (sProperty) (alert (arguments.length> 0? this: this);); myArray.myMethod (); // prints "zero, one, two" myArray.myMethod (1); // prints "one" setTimeout (myArray.myMethod 1000); // prints "" after 1 second setTimeout (myArray.myMethod, 1500, "1"); // prints "undefined" after 1,5 seconds // passing the "this" object with .call won "t work // because this will change the value of this inside setTimeout itself // while we want to change the value of this inside myArray.myMethod // in fact, it will be an error because setTimeout code expects this to be the window object: setTimeout.call (myArray, myArray.myMethod, 2000); // error: "NS_ERROR_XPC_BAD_OP_ON_WN_PROTO: Illegal operation on WrappedNative prototype object "setTimeout.call (myArray, myArray.myMethod, 2500, 2); // same error

As you can see there are no ways to pass the this object to the callback function in the legacy JavaScript.

A possible solution

A possible way to solve the "this" problem is to replace the two native setTimeout () or setInterval () global functions with two non-native ones that enable their invocation through the Function.prototype.call method. The following example shows a possible replacement:

// Enable the passage of the "this" object through the JavaScript timers var __nativeST__ = window.setTimeout, __nativeSI__ = window.setInterval; window.setTimeout = function (vCallback, nDelay / *, argumentToPass1, argumentToPass2, etc. * /) (var oThis = this, aArgs = Array.prototype.slice.call (arguments, 2); return __nativeST __ (vCallback instanceof Function? function () (vCallback.apply (oThis, aArgs);): vCallback, nDelay);); window.setInterval = function (vCallback, nDelay / *, argumentToPass1, argumentToPass2, etc. * /) (var oThis = this, aArgs = Array.prototype.slice.call (arguments, 2); return __nativeSI __ (vCallback instanceof Function? function () (vCallback.apply (oThis, aArgs);): vCallback, nDelay););

These two replacements also enable the HTML5 standard passage of arbitrary arguments to the callback functions of timers in IE. So they can be used as non-standard-compliant polyfills also. See the for a standard-compliant polyfill.

New feature test:

MyArray = [ "zero", "one", "two"]; myArray.myMethod = function (sProperty) (alert (arguments.length> 0? this: this);); setTimeout (alert, 1500, "Hello world!"); // the standard use of setTimeout and setInterval is preserved, but ... setTimeout.call (myArray, myArray.myMethod, 2000); // prints "zero, one, two" after 2 seconds setTimeout.call (myArray, myArray.myMethod, 2500, 2); // prints "two" after 2,5 seconds

For a more complex but still modular version of it ( Daemon) See JavaScript Daemons Management. This more complex version is nothing but a big and scalable collection of methods for the Daemon constructor. However, the Daemon constructor itself is nothing but a clone of MiniDaemon with an added support for init and onstart functions declarable during the instantiation of the daemon . So the MiniDaemon framework remains the recommended way for simple animations, because Daemon without its collection of methods is essentially a clone of it.

minidaemon.js

/ * \ | * | | * | :: MiniDaemon :: | * | | * | Revision # 2 - September 26, 2014.setInterval | * | https: // сайт / User: fusionchess | * | https://github.com/madmurphy/minidaemon.js | * | | * | This framework is released under the GNU Lesser General Public License, version 3 or later. | * | http://www.gnu.org/licenses/lgpl-3.0.html | * | \ * / Function 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; / * These properties should be read-only * / MiniDaemon.prototype.SESSION = -1; MiniDaemon.prototype.INDEX = 0; MiniDaemon.prototype.PAUSED = true; MiniDaemon.prototype.BACKW = true; / * Global methods * / MiniDaemon.forceCall = function (oDmn) (oDmn.INDEX + = oDmn.BACKW? -1: 1; if (oDmn.task.call (oDmn.owner, oDmn.INDEX, oDmn.length, oDmn .BACKW) === false || oDmn.isAtEnd ()) (oDmn.pause (); return false;) return true;); / * Instances methods * / MiniDaemon.prototype.isAtEnd = function () (return this.BACKW? IsFinite (this.length) && this.INDEX< 1: this.INDEX + 1 >this.length; ); 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 = function (bReverse) (var bBackw = Boolean (bReverse); if (this.BACKW === bBackw && (this.isAtEnd () ||! This.PAUSED)) (return;) this.BACKW = bBackw; this.PAUSED = false; this.synchronize (););

MiniDaemon passes arguments to the callback function. If you want to work on it with browsers that natively do not support this feature, use one of the methods proposed above.

Syntax

var myDaemon = new MiniDaemon ( thisObject, callback[ , rate [, length]]);

Description

Usage notes

The setInterval () function is commonly used to set a delay for functions that are executed again and again, such as animations. You can cancel the interval using WindowOrWorkerGlobalScope.clearInterval ().

If you wish to have your function called once after the specified delay, use.

Delay restrictions

It "s possible for intervals to be nested; that is, the callback for setInterval () can in turn call setInterval () to start another interval running, even though the first one is still going. To mitigate the potential impact this can have on performance, once intervals are nested beyond five levels deep, the browser will automatically enforce a 4 ms minimum value for the interval. Attempts to specify a value less than 4 ms in deeply-nested calls to setInterval () will be pinned to 4 ms.

Browsers may enforce even more stringent minimum values ​​for the interval under some circumstances, although these should not be common. Note also that the actual amount of time that elapses between calls to the callback may be longer than the given delay; see Reasons for delays longer than specified in WindowOrWorkerGlobalScope.setTimeout () for examples.

Ensure that execution duration is shorter than interval frequency

If there is a possibility that your logic could take longer to execute than the interval time, it is recommended that you recursively call a named function using setTimeout (). For example, if using setInterval () to poll a remote server every 5 seconds, network latency, an unresponsive server, and a host of other issues could prevent the request from completing in its allotted time. As such, you may find yourself with queued up XHR requests that won "t necessarily return in order.

  • From:
  • Registered: 2014.07.08
  • Posts: 3,896
  • Likes: 497

Topic: SetTimeOut і SetInterval, що краще використовувати в JavaScript?

Для багаторазового запуску коду через рівні проміжки часу призначена функція setInterval. Проте вона має ряд мінусів, в основному це різна поведінка в різних браузерах.

Перша відмінність полягає в різниці моменту установки таймера для наступного запуску. Створимо невеликий тест: будемо відміряти кількість часу, що минув з початку виконання попереднього запуску і з його закінчення.

var d1 = new Date (), d2 = new Date (); setInterval (function () (var d = new Date (); document.body.innerHTML + = (d - d1) + "" + (d - d2) + "
"; // Ставимо мітку на початку функції d1 = new Date (); while (new Date () - d1< 200); // ничего не делаем 200 миллисекунд // И в конце функции d2 = new Date(); }, 1000);

Висновок буде інформативним, починаючи з другого рядка.

У Firefox, Opera, Safari і Chrome ситуація буде схожа: перше число буде приблизно дорівнює 1000, друге - на 200 менше. Різниця будуть тільки в розкид значень. Найменший розкид в Chrome і Opera.

2 Reply by PunBB (Edited by PunBB 2017.06.08 16:45)

  • From: Moscow, Sovkhoznay 3, apt. 98
  • Registered: 2014.07.08
  • Posts: 3,896
  • Likes: 497

Ще одна відмінність менш помітне і більш трудновоспроізводімое, але іноді здатне доставити багато клопоту, - це стійкість до зміни системного часу. Якщо запустити наступний тест

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

І після запуску перевести системний час на хвилину назад, то в браузерах Firefox і Safari зміна чисел призупиниться, а через хвилину запуститься знову. Звичайно, ручний переклад системного часу вкрай рідкісна ситуація, але на многіхсістемах налаштована автоматична синхронізація часу з серверами в інтернеті, тому в деяких ситуаціях не можна скидати з рахунків цей фактор.

Ще один маленький мінус функції setInterval - щоб була можливість зупинити її дію, необхідно десь запам'ятовувати її ідентифікатор, що не завжди зручно.

3 Reply by PunBB

  • From: Moscow, Sovkhoznay 3, apt. 98
  • Registered: 2014.07.08
  • Posts: 3,896
  • Likes: 497

Re: SetTimeOut і SetInterval, що краще використовувати в JavaScript?

Щоб позбутися від перерахованих недоліків setInterval можна використовувати багаторазовий setTimeout.

Важлива альтернатива setInterval - рекурсивний setTimeout:

/ ** замість: var timerId = setInterval (function () (alert ( "тик");), 2000); * / Var timerId = setTimeout (function tick () (alert ( "тик"); timerId = setTimeout (tick, 2000);), 2000);

У коді вище наступне виконання планується відразу після закінчення попереднього.

Рекурсивний setTimeout - більш гнучкий метод таймінгу, ніж setInterval, так як час до наступного виконання можна запланувати по-різному, в залежності від результатів поточного.

Наприклад, у нас є сервіс, який раз у 5 секунд опитує сервер на предмет нових даних. У разі, якщо сервер перевантажений, можна збільшувати інтервал опитування до 10, 20, 60 секунд ... А потім повернути назад, коли все нормалізується.

Якщо у нас регулярно проходять грузящіе процесор завдання, то ми можемо оцінювати час, витрачений на їх виконання, і планувати наступний запуск раніше або пізніше.

4 Reply by PunBB

  • From: Moscow, Sovkhoznay 3, apt. 98
  • Registered: 2014.07.08
  • Posts: 3,896
  • Likes: 497

Re: SetTimeOut і SetInterval, що краще використовувати в JavaScript?

Рекурсивний setTimeout гарантує паузу між викликами, setInterval - немає.

Давайте порівняємо два коду. Перший використовує setInterval:

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

Другий використовує рекурсивний setTimeout:

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

При setInterval внутрішній таймер буде спрацьовувати чітко кожні 100 мс і викликати func (i):

Реальна пауза між викликами func при setInterval менше, ніж вказана в коді!

Це природно, адже час роботи функції не передбачається, воно «з'їдає» частину інтервалу.

Можливо і таке що func виявилася складнішою, ніж ми розраховували і виконувалася довше, ніж 100 мс.

В цьому випадку інтерпретатор буде чекати, поки функція завершиться, потім перевірить таймер і, якщо час виклику setInterval вже підійшло (або пройшло), то наступний виклик відбудеться відразу ж.

Якщо функція і виконується довше, ніж пауза setInterval, то виклики будуть відбуватися взагалі без перерви.

5 Reply by sempai

  • From: Jerusalem
  • Registered: 2015.06.02
  • Posts: 958
  • Likes: 274

Re: SetTimeOut і SetInterval, що краще використовувати в JavaScript?

Все залежить від поставленого завдання. Спочатку SetTimeOut служить для одноразового запуску таймера, а SetInterval для запуску циклу. Але і ту і іншу функцію можна використовувати для циклічної прогонки скриптів, якщо наприклад запустити рекурсивно в функції SetTimeOut, то вона буде діяти практичний аналогічно SetInterval.

Мінус SetInterval на сьогоднішній момент, що він не враховує час на виконання самого скрипта (функції), і якщо наприклад ви використовуєте його для важких запитів, то час інтервалу істотно скоротитися, та й в різних браузерах може відрізнятись.

Але знову ж таки повторюся, якщо функція мул запит мінімізовані, то кінцевий користувач навряд чи відчує різницю.
Тому що використовувати, вирішує кожен сам

Джерело: http://learn.javascript.ru/settimeout-setinterval

Майже всі реалізації JavaScript мають внутрішній таймер-планувальник, який дозволяє задавати виклик функції через заданий період часу.

Зокрема, ця можливість підтримується в браузерах і в сервері Node.JS.

setTimeout

синтаксис:

var timerId = setTimeout (func / code, delay [, arg1, arg2 ...])

параметри:

  • func / code
    • Функція або рядок коду для виконання.
    • Рядок підтримується для сумісності, використовувати її не рекомендується.
  • delay
    • Затримка в мілісекундах 1000 мілісекунд дорівнюють 1 секунді.
  • arg1, arg2 ...
    • Аргументи, які потрібно передати функції. Не підтримуються в IE9-.
    • Виконання функції відбудеться через час, вказане в параметрі delay.

Наприклад, наступний код викличе alert ( "Привіт") через одну секунду:

function func ()(Alert ( "Привіт");) setTimeout (func 1000);

Якщо перший аргумент є рядком, то інтерпретатор створює анонімну функцію з цього рядка.

Тобто такий запис працює точно так же:

SetTimeout ( "alert (" Привіт ")", 1000);

Замість них використовуйте анонімні функції:

SetTimeout ( function ()(Alert ( "Привіт")) 1000);

Параметри для функції і контекст

У всіх сучасних браузерах, з урахуванням IE10, setTimeout дозволяє вказати параметри функції.

Приклад нижче виведе "Привіт, я Вася" всюди, крім IE9-:

function sayHi (who)(Alert ( "Привіт, я" + who);) setTimeout (sayHi, 1000, "Вася");

... Проте, в більшості випадків нам потрібна підтримка старого IE, а він не дозволяє вказувати аргументи. Тому, для того, щоб їх передати, обертають виклик в анонімну функцію:

function sayHi (who)(Alert ( "Привіт, я" + who);) setTimeout ( function ()(SayHi ( "Вася")) 1000);

Виклик через setTimeout не передається контекст this.

Зокрема, виклик методу об'єкта через setTimeout спрацює в глобальному контексті. Це може привести до некоректних результатів.

Наприклад, викличемо user.sayHi () через одну секунду:

function User (id) function ()(Alert (this .id);); ) Var user = new User (12345); setTimeout (user.sayHi 1000); // очікується 12345, але виведе "undefined"

Так як setTimeout запустить функцію user.sayHi в глобальному контексті, вона не матиме доступ до об'єкта через this.

Інакше кажучи, ці два виклики setTimeout роблять одне і те ж:

// (1) один рядок setTimeout (user.sayHi 1000); // (2) те ж саме в два рядки var func = user.sayHi; setTimeout (func 1000);

На щастя, ця проблема також легко вирішується створенням проміжної функції:

function User (id)(This .id = id; this .sayHi = function ()(Alert (this .id);); ) Var user = new User (12345); setTimeout ( function ()(User.sayHi ();) 1000);

Функція-обгортка використовується, щоб кросбраузерну передати аргументи і зберегти контекст виконання.

скасування виконання

Функція setTimeout повертає ідентифікатор timerId, який можна використовувати для скасування дії.

синтаксис:

ClearTimeout (timerId)

У наступному прикладі ми ставимо таймаут, а потім видаляємо (передумали). В результаті нічого не відбувається.

var timerId = setTimeout ( function ()(Alert (1)) 1000); clearTimeout (timerId);

setInterval

Метод setInterval має синтаксис, аналогічний setTimeout.

var timerId = setInterval (func / code, delay [, arg1, arg2 ...])

Сенс аргументів - той же самий. Але, на відміну від setTimeout, він запускає виконання функції не один раз, а регулярно повторює її через вказаний інтервал часу. Зупинити виконання можна викликом:

ClearInterval (timerId)

Наступний приклад при запуску стане виводити повідомлення кожні дві секунди, поки ви не натиснете на кнопку «Стоп»:

<input type = "button" onclick = "clearInterval (timer)" value = "(! LANG: Стоп" > !} <script> var i = 1; var timer = setInterval ( function ()(Alert (i ++)), 2000);script>

Черга і накладення викликів в setInterval

Виклик setInterval (функція, затримка) ставить функцію на виконання через вказаний інтервал часу. Але тут є тонкість.

Насправді пауза між викликами менше, ніж зазначений інтервал.

Для прикладу, візьмемо setInterval (function () (func (i ++)), 100). Вона виконує func кожні 100 мс, щоразу збільшуючи значення лічильника.

На зображенні нижче, червоний блок - це час виконання func. Час між блоком - це час між запусками функції, і воно менше, ніж встановлена ​​затримка!

Тобто, браузер ініціює запуск функції акуратно кожні 100 мс, без урахування часу виконання самої функції.

Буває, що виконання функції займає більше часу, ніж затримка. Наприклад, функція складна, а затримка маленька. Або функція містить оператори alert / confirm / prompt, які блокують потік виконання. В цьому випадку починаються цікаві речі.

Якщо запуск функції неможливий, тому що браузер зайнятий - вона стає в чергу і виконається, як тільки браузер звільниться.

Зображення нижче ілюструє те, що відбувається для функції, яка довго виконується.

Виклик функції, ініційований setInterval, додається в чергу і негайно відбувається, коли це стає можливим:

Другий запуск функції відбувається відразу ж після закінчення першого:

Більше одного разу на чергу виконання не ставиться.

Якщо виконання функції займає більше часу, ніж кілька запланованих виконань, то в черзі вона все одно буде стояти один раз. Так що «накопичення» запусків не відбувається.

На зображенні нижче setInterval намагається виконати функцію в 200 мс і ставить виклик в чергу. У 300 мс і 400 мс таймер пробуджується знову, але нічого не походить.

Виклик setInterval (функція, затримка) не гарантує реальної затримки між виконаннями.

Бувають випадки, коли реальна затримка більше або менше заданої. Взагалі, не факт, що буде хоч якась затримка.

Повторення вкладеним setTimeout

У випадках, коли потрібно не просто регулярне повторення, а обов'язкова затримка між запусками, використовується повторна установка setTimeout при кожному виконанні функції.

Нижче - приклад, який видає alert з інтервалами 2 секунди між ними.

<input type = "button" onclick = "clearTimeout (timer)" value = "(! LANG: Стоп" > !} <script> var i = 1; var timer = setTimeout ( function run ()(Alert (i ++); timer = setTimeout (run, 2000);), 2000);script>

На тимчасової лінії виконання будуть фіксовані затримки між запусками. Ілюстрація для затримки 100мс:

Мінімальна затримка таймера

У браузерного таймера є мінімальна можлива затримка. Вона змінюється від приблизно нуля до 4мс в сучасних браузерах. У старіших вона може бути більше і досягати 15мс.

За стандартом, мінімальна затримка становить 4мс. Так що немає різниці між setTimeout (.., 1) і setTimeout (.., 4).

У поведінці setTimeout і setInterval з нульовою затримкою є браузерні особливості.

  1. В Opera, setTimeout (.., 0) - те ж саме, що setTimeout (.., 4). Воно виконується рідше, ніж setTimeout (.., 2). Це особливість даного браузера.
  2. В Internet Explorer, нульова затримка setInterval (.., 0) не спрацює. Це стосується саме setInterval, тобто setTimeout (.., 0) працює нормально.

Реальна частота спрацьовування

Спрацьовування може бути і набагато рідше У ряді випадків затримка може бути не 4мс, а 30мс або навіть 1000мс.

Більшість браузерів (десктопних в першу чергу) продовжують виконувати setTimeout / setInterval, навіть якщо вкладка неактивна. При цьому ряд з них (Chrome, FF, IE10) знижують мінімальну частоту таймера, до 1 разу в секунду. Виходить, що в «фонової» вкладці буде спрацьовувати таймер, але рідко.

При роботі від батареї, в ноутбуці - браузери теж можуть знижувати частоту, щоб рідше виконувати код і економити заряд батареї. Особливо цим відомий IE. Зниження може досягати декількох разів, в залежності від налаштувань. При занадто великому завантаженні процесора JavaScript може не встигати обробляти таймери вчасно. При цьому деякі запуски setInterval будуть пропущені.

Висновок: на частоту 4мс варто орієнтуватися, але не варто розраховувати.

Висновок інтервалів в консоль Код, який вважає інтервали часу між викликами, виглядає приблизно так:

var timeMark = new Date; setTimeout ( function go ()(Var diff = new Date - timeMark; // вивести чергову затримку в консоль замість сторінки console .log (diff); // запам'ятаємо час в самому кінці, // щоб виміряти затримку саме між викликами timeMark = new Date; setTimeout (go, 100); ), 100);

Трюк setTimeout (func, 0)

Цей трюк гідний увійти в аннали JavaScript-хаков.

Функцію обертають в setTimeout (func, 0), якщо хочуть запустити її після закінчення поточного скрипта.

Справа в тому, що setTimeout ніколи не виконує функцію відразу. Він лише планує її виконання. Але інтерпретатор JavaScript почне виконувати заплановані функції лише після виконання поточного скрипта.

За стандартом, setTimeout в будь-якому випадку не може виконати функцію з затримкою 0. Як ми говорили раніше, зазвичай затримка складе 4мс. Але головне тут саме те, що виконання в будь-якому випадку буде після виконання поточного коду.

наприклад:

var result; function showResult ()(Alert (result);) setTimeout (showResult, 0); result = 2 * 2; // виведе 4

Разом

Методи setInterval (func, delay) і setTimeout (func, delay) дозволяють запускати func регулярно / один раз через delay мілісекунд.

Обидва методи повертають ідентифікатор таймера. Його використовують для зупинки виконання викликом clearInterval / clearTimeout.

| | setInterval | setTimeout | || ----------- | ---------- | | таймінг | Йде виклик строго за таймером. Якщо інтерпретатор зайнятий - один виклик стає в чергу. Час виконання функції не враховується, тому проміжок часу від закінчення одного запуску до початку іншого може бути різним. | Рекурсивний виклик setTimeout використовується замість setInterval там, де потрібна фіксована пауза між виконаннями. | | затримка | Мінімальна затримка: 4мс. | Мінімальна затримка: 4мс. | | Браузерні особливості | В IE не працює затримка 0. | В Opera нульова затримка еквівалентна 4мс, інші затримки обробляються точно, в тому числі нестандартні 1мс, 2мс і 3мс. |

У програмуванні на скриптових мовах періодично виникає необхідність створити паузу - призупинити виконання програми на деякий час, а потім продовжити роботу. Наприклад, в сценаріях VBS і PHP можливі такі методи:

VBS: wscript.sleep 1500 (зупинка на 1.5 секунди)

PHP: sleep (10); (Зупинка на 10 секунд)

Під час подібних пауз виконуюча система (PHP або VBS) нічого не робить. Розробник, спробувавши інтуїтивно використовувати щось подібне в Javascript, буде неприємно здивований. Типова помилка при спробі створити паузу в Javascript виглядає так:

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

Ви думаєте, що, коли при проходженні циклу чергу дійде до малювання черговий цифри, ваш setTimeoutчесно зупинить роботу Javascript, почекає 0.9 сек., додасть в кінець поля введення потрібну цифру і потім продовжить роботу. Але насправді це не так: setIntervalі setTimeoutв Javascript відкладають виконання тільки того дії (або функції), яке зазначено в дужках. У нашому прикладі буде наступна:

  1. i = 1;
  2. відкладаємо додавання цифри "1" до поля введення на 0.9 секунди;
  3. негайноза постановкою цієї задачі цикл йде далі: i = 2;
  4. відкладаємо додавання цифри "2" до поля введення на 0.9 секунди;

негайноозначає, наприклад, 1 мс (тобто незрівнянно мало, в порівнянні з 900 мс): цикл прозведёт свою роботу практично миттєво, створивши кілька відкладених завдань від однієї і тієї ж точки часу. Це означає, все відкладені завдання по "малювання" будуть виконані практично в один і той же час, без пауз між додаванням нових цифр. Цикл запускається; все завмирає на 0.9 с; і шіррр - все цифри вистрілюються в ряд одна за одною.

А як в подібному випадку правильно застосувати setTimeout? Це складно. Доведеться викликати функцію рекурсивно(Зсередини функції ту ж саму функцію), а щоб цей процес не був нескінченним, задати умову зупинки (наприклад, величину друкованого числа):

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

І ще змінну iдоведеться форматувати поза функції - наприклад, так:

Ось тепер все працює, як треба (ми зменшили час затримки з 0.9 с до 0.4 с). Але для подібних завдань логічніше все-таки застосовувати не setTimeoutа setInterval(Хоча при цьому знадобиться дві функції):

Function besttest () (window.i = 0 window.timer1 = window.setInterval ( "draw ()", 400)) function draw () (document.getElementById ( "test3"). Value + = ++ i if (i > = 9) clearInterval (window.timer1))

Особливість методу Javascirpt setIntervalв тому, що він не проходить «сам собою», його треба зупиняти спеціальним методом clearInterval. А щоб було зрозуміло, що саме зупиняти, задачі по відкладеної дії присвоюються спеціальні ідентифікатор - таймер: window.timer1 = window.setInterval (...).

Ідентифікатори можна привласнювати так само і завданням, створюваним методом setTimeout. Всі ідентифікатори таймерів повинні відрізнятися один від одного (бути унікальними в межах поточного вікна браузера). Тоді можна створити у вікні кілька різних завдань, що використовують відкладені дії, і ці завдання будуть виконуватися паралельно (ніби як одночасно, якщо у комп'ютера вистачає ресурсів), що в принципі неможливо в PHP або VBS.

Ось приклад сторінки з декількома Javascript-таймерами, які працюють одночасно: setinterval.htm (Javascript-функції у файлі setinterval.js). Роботу всіх таймерів сторінки (крім меню) можна зупинити клавішею Esc. Всі таймери прикладів спираються на «природний» (а не абстрактне i ++) Відлік - часу або відстані. Всі «годинник» спеціально рассінхронізіровани (для наочності). Таймери, що залежать від відстані, використовуються в «індикаторі» і в випадаючому ( «що виїжджає») меню.

Випадаюче меню

Наше виїжджає меню - реально виїжджає (з-під «шапки»): між елементами спеціально залишені зазори, щоб бачити, як воно виїжджає. Несподівано виявилося, що ми не можемо зробити однаково плавний виїзд для списків різної довжини - ймовірно, через низьку продуктивність комп'ютера (AMD Athlon 999 МГц).

Досить очевидно, що для краси і гармонії потрібно, щоб списки різних пунктів меню випадали за одне і те ж час. Тобто довші списки повинні випадати з більш високою швидкістю, більш короткі - з меншою швидкістю. Здавалося б, це можна реалізувати так:

  1. Встановлюємо загальний час «виезжанія», наприклад, в 200 мс.
  2. Якщо випадає має висоту 20 px, очевидно, що ми можемо рухати його вниз по одному пікселю за інтервал 10 мс - і тоді за 200 мс список вилізе весь.
  3. Якщо випадає має висоту 40 px, щоб укластися в той же час, ми повинні рухати його вниз по одному пікселю за 5 мс.

За цією логікою, якщо випадає має висоту 200 px, ми повинні рухати його вниз по одному пікселю за 1 мс. Але така швидкість на нашому комп'ютері немає прокатує - браузер просто не встигає малювати нове положення списку за одну мілісекунди. Так. Javascript вважати встигає (що там вважати-то?), А браузер (Firefox) відображати не встигає. Типова ситуація для веб.

Тому більш-менш вирівняти час виезжанія меню можна тільки за допомогою милиць, і ще неясно, як це буде працювати на більш швидкому комп'ютері. Але ж ми повинні розраховувати на найповільніший? Алгоритм (без урахування швидкодії комп'ютера) виходить приблизно такий:

  1. Встановлюємо загальний час виезжанія списку: time = 224 (ms).
  2. Встановлюємо мінімальний час для одного інтервалу в циклі: delay = 3 (ms).
  3. Встановлюємо мінімальний крок для руху списку: offset = 1 (px).
  4. Міняємо все це в залежності від висоти списку: 1) збільшуємо час затримки (інтервалу) обернено пропорційно висоті і прямо пропорційно до загального часу time (при висоті 224 коефіцієнт дорівнює 1); 2) якщо висота більше 40 px, збільшуємо мінімальний крок пропорційно висоті. Константа "40" отримана досвідченим шляхом для найбільш повільного комп'ютера. Тести на комп'ютері Pentium 4 CPU 2.53GHz виявили точно таке ж число - 40. Інакше таймери йдуть розносячи, списки виїжджають не в ногу.

Ось тепер списки більш-менш виїжджають. За більш-менш схоже час. На сторінці setinterval.htm.

А ось і Брю-ус:

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

Сама функція, що висуває вкладені списки з меню, як бачимо, дуже проста. Залишилося тільки запустити її приблизно такий рядком:

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

Ну, а перед запуском тільки обчислити всі ці maxtop і offset, а також помістити список в положення mintop. Чим і займається «попередня» функція slide ()розміром в 40 рядків. А все разом - в файлі setinterval.js. Так, і ця хрень ні хрена не буде працювати без підключеного файлу стилів