Повторювати дії через заданий інтервал часу JavaScript. Приклади функції jQuery-setTimeout(). Супутні проблеми та “this”

Головна / Усунення несправностей

The setInterval()метод, розташований на window and worker interfaces, repeatedly calls a function or executes a code snippet, with a fixed time delay between each call.Це повертається в інтервал ID, який необов'язково identifies the interval, так що можна перейти до його терміну за допомогою callinterval() . Цей метод визначається за допомогою WindowsOrWorkerGlobalScope 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. Ця функція не повторюється будь-якою думкою, а не відновлене значення є виявленою. код За допомогою опційної syntax можна включити string instead of function, який є складним і виконаним кожним рівнем мільйонівкон. Цей syntax is not recommended for the same reasons that make using eval() a security risk. delay The time, в milliseconds (тисячіі з двох), the timer should delay in between executions of the specified function or code. Натисніть на деталі на визначених рівнях delay values. arg1, ..., argN Optional Additional arguments which are passed through to the function specified by func once the timer expires.

Note: Просування додаткових arguments до setInterval() в першій syntaxі не працює в Internet Explorer 9 і earlier. Якщо ви намагаєтеся надати цю функціональність на тому, що браузер, ви повинні використовувати polyfill (see the section).

Return value

Відновлений intervalID є numerical, non-zero value which identifies the timer created by the call to setInterval() ; this value can be passed to to cancel the timeout.

Це може бути ефективним для того, щоб налаштувати, що setInterval() і setTimeout() share the sam pool of IDs, and clearInterval() and clearTimeout() can technically be used interchangeably. Для clarity, however, ви повинні намагатися до будь-якого матчу ним до avoid confusion коли maintaining ваш код.

Note: The delay argument is converted to a signed 32-bit integer Це ефективні терміни складає до 2147483647 ms, тому що він відповідає як ідентифікатор в IDL.

Examples

Example 1: Basic syntax

Наступні приклади демонструють 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

Наступні приклади повідомлень про flashtext() функцію once second until Stop button is pressed.

setInterval/clearInterval example

Hello World

Example 3: Typewriter simulation

Наступні приклади simulates typewriter by clearing and then slowly typing content into 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 у 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, no viverra justo diam eget arcu. Phasellus adipiscing fermentum nibh ac commodo. Nam turpis nunc, suscipit a hendrerit vitae, volutpat неn ipsum.

Phasellus ac nisl lorem:

Duis lobortis sapien quis nisl luctus porttitor. У період 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 в 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, у 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 acccsan 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

Як попередньо розглянути, браузер Internet Explorer 9 і далі не підтримує проходження argumentів до callback функцій в її setTimeout() or setInterval() . The following IE-specificкоди демонструють метод для overcoming цього обмеження. Для того, щоб використовувати, скориставшись наступним кодом до верхнього вікна 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 aArg .prototype.slice.call(arguments, 2); if (document.all && !window.setInterval.isPolyfill) ( var __nativeSI__ = window.setInterval; window.setInterval = функція (vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */) ( var aArgs = Ar slice.call(arguments, 2);

Інші можливості є для використання anonymous функцій, щоб call your callback, алеякщо це рішення є 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), терміни є clamped to fire no more often than once per second in inactive tabs.

The " this " problem

Якщо ви проймете метод до setInterval() або будь-якої іншої функції, він є внесений з відповідним цим значенням. Цей питання є розкритим в detail в JavaScript reference .

Explanation

Код executed by setInterval() runs in a separate execution context than the function from which it was called. Як наслідок, це ключове слово для здійсненої функції є налаштувати на window (або Global) об'єкт, який не є тим самим, як це значення для функції, що називається setTimeout . Натисніть на наступному прикладі (який використовує setTimeout() instead of setInterval() – питання, в дійсності, є саме для всіх таймерів):

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 // в fact, він повинен бути неправильним для setTimeout code expects this to be the window object: setTimeout.call(myArray, myArray.myMethod, 2000); // error: "NS_ERROR_XPC_BAD_OP_ON_WN_PRO object" setTimeout.call(myArray, myArray.myMethod, 2500, 2); // same error

Як ви можете бачити, що немає способів пройти цей об'єкт до callback функцій в союзності JavaScript.

A possible solution

A можливо, щоб вирішити " цей " питання є замінити два природні setTimeout() or setInterval() global functions with two non-native ones that enable their invocation через Function.prototype.call метод. The following example shows a possible replacement:

// Дозволяє перейти до "цього" об'єкта через 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 in? () ( 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 in () ( vCallback.apply(oThis, aArgs); ) : vCallback, nDelay); );

Ці дві заміни також здатні до HTML5 стандартного проходження arbitrary arguments to callback functions 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!"); // Стандартний використання setTimeout і 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 more complex but still modular version of it ( Daemon) JavaScript JavaScript Daemons Management . Ця більша складна версія не є тільки великою і широкою колекцією методів для Daemonконструктор. However, the Daemonконструктор йогоself is nothing but a clone of MiniDaemon with an added support for init and onstartфункції declarable протягом instantiation of the daemon . So the MiniDaemon framework remains the recommended way for simple animations, because Daemonбез його колекції методів є помітно clone of it.

minidaemon.js

/*\ |*| |*| :: MiniDaemon:: |*| |*| Revision #2 - September 26, 2014.setInterval |*| https://сайт/User:fusionchess |*| https://github.com/madmurphy/minidaemon.js |*| |*| Цей framework виконаний під 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 = функція (oDmn) (oDmn.INDEX += oDmn.BACKW ? -1: 1; if (oDmn.task.call(oDmn.owner, oDmn.INDEX, oDmn.length, oDmn. .BACKW) === false || oDmn.isAtEnd()) ( oDmn.pause(); /* 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 до callback function. Якщо ви збираєтеся працювати на ньому з браузерами, які природно не підтримують цю особливість, використовуйте один з методів, що розкриваються.

Syntax

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

Description

Usage notes

SetInterval() функція, як правило, використовується для набору дій для функцій, які є виконаними, і так, як і animations. Ви можете відключити час, використовуючи WindowsOrWorkerGlobalScope.clearInterval() .

If you wish to have your function називається once after the specified delay, use .

Delay restrictions

Це "можливе для термінів, щоб бути невтішним; що є, callback для setInterval() може в turn call setInterval() для запуску іншого терміну бігу, навіть якщо першим одним буде йти. Performance, once intervals are nested beynd five levels deep, the browser will automatically enforce a 4 ms minimum value for the interval.

Браузери можуть знати, що навіть більше stringent мінімальні значення для терміну під деякими circumstances, хоча вони повинні не бути спільними. Note also that the actual amount of time that elapses between calls to callback may be longer than given delay ; Виберіть Reasons for delays longer than specified in WindowOrWorkerGlobalScope.setTimeout() for examples.

Ensure that execution duration is shorter than interval frequency

Якщо є можливість, що ваш логічний може виконати тривалість до виконання лише в часі часу, це є відповідним тим, що ви є recursive call і наведена функція використовуючи setTimeout() . Для прикладу, якщо використовує setInterval() до поло на remote server протягом 5 seconds, мережевий термін, на невідповідному сервері, і host з інших повідомлень може запропонувати відповідь від завершення в його тривалий час. Як так, вам може бути виявлено, що ви потребуєте XHR потреби, які необхідно відновити в порядку.

Джерело: 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 у глобальному контексті, вона не матиме доступу до об'єкта через це .

Інакше кажучи, ці два виклики 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. Так, і ця хрень ні хрону не працюватиме без підключеного файлу стилів

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

  • var id = setTimeout(fn, delay); - Створює простий таймер, який викличе цю функцію після заданої затримки. Функція повертає унікальний ID, за допомогою якого таймер може призупинитися.
  • var id = setInterval(fn, delay); - Схоже на setTimeout, але безперервно викликає функцію із заданим інтервалом (поки не буде зупинена).
  • clearInterval(id);, clearTimeout(id); - Приймає таймер ID (повертається однією з функцій, описаних вище) та зупиняє виконання callback"a.
Головна ідея, яку слід розглянути, полягає в тому, що точність періоду затримки таймера не гарантується. Почнемо з того, що браузер виконує всі асинхронні JavaScript-події в одному потоці (такі як клік мишею або таймери) і тільки в той час, коли настала черга цієї події. Найкраще це демонструє наступна діаграма:

На цьому малюнку досить багато інформації, яку потрібно засвоїти, але розуміння дасть вам глибше розуміння механізму роботи асинхронності виконання JavaScript. на цій діаграмі вертикально представлений час у мілісекундах, сині блоки показують блоки JavaScript коду, який було виконано. Наприклад, перший блок виконується загалом за 18мс, клік мишею блокує виконання приблизно 11мс тощо.

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

Для початку уявімо, що всередині JavaScript блоку стартують два таймери: setTimeout із затримкою 10мс і setInterval з такою самою затримкою. Залежно від того, коли стартує таймер, він спрацює у момент, коли ми ще не завершили перший блок коду. Зауважте, однак, що він не спрацьовує відразу (це неможливо через однопотоковість). Натомість відкладена функція потрапляє у чергу і виконується у наступний доступний момент.

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

Після того, як перший блок JavaScript коду був виконаний, браузер задається питанням «Що чекає на виконання?». У цьому випадку обробник кліка мишею та таймер перебувають у стані очікування. Браузер вибирає один з них (обробник кліка) та виконує його. Таймер чекатиме наступної доступної порції часу у черзі на виконання.

Зауважте, що поки обробник кліка мишею виконується, спрацьовує перший interval-callback. Так само як і timer-callback, він буде поставлений у чергу. Проте, врахуйте, що коли знову спрацює interval (поки виконуватиметься timer-callback), він буде видалено з черги. Якби всі interval-callback" і потрапляли в чергу поки виконується великий шматок коду, це призвело б до того, що утворилася б купа функцій, що чекають виклику без періодів затримок між закінченням їх виконання. Натомість браузери прагнуть чекати поки не залишиться жодної функції у черзі перш ніж додати до черги ще одну.

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

Нарешті, після того як другий interval-callback завершиться, ми побачимо, що не залишилося нічого, що JavaScript-движок повинен виконати. Це означає, що браузер знову чекає на появу нових асинхронних подій. Це станеться на позначці 50мс, де interval-callback спрацює знову. У цей момент не буде нічого, що блокувало б його, тому він спрацює негайно.

Давайте розглянемо приклад, який добре ілюструє різницю між setTimeout та setInterval.
setTimeout(function()( /* Some long block of code... */ setTimeout(arguments.callee, 10); ), 10); setInterval(function()( /* Some long block of code... */ ), 10);
Ці два варіанти є еквівалентними на перший погляд, але насправді це не так. Код, що використовує setTimeout завжди матиме затримку хоча б 10мс після попереднього виклику (він може бути більшим, але ніколи не може бути меншим), тоді як код, що використовує setInterval, буде прагнути викликатися кожні 10мс незалежно від того, коли відпрацював попередній виклик.

Давайте резюмуємо все сказане вище:
- JavaScript движки використовують однопоточне середовище, перетворюючи асинхронні події в чергу, що очікує виконання,
- Функції setTimeout та setInterval принципово по-різному виконуються в асинхронному коді,
- Якщо таймер не може бути виконаний в даний момент, він буде відкладений до наступної точки виконання (яка буде довшою за бажану затримку),
- Інтервали (setInterval) можуть виконуватися один за одним без затримок, якщо їхнє виконання займає більше часу, ніж зазначена затримка.

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

© 2022 androidas.ru - Все про Android