Разное

Node js setinterval: Таймеры в Node.js | Node.js

Содержание

Таймеры в Node.js | Node.js

Edit on GitHub

Модуль таймеров в Node.js содержит функции, которые выполняют код по истечении
заданного периода времени. Таймеры не нужно импортировать через require(), так как
все методы доступны глобально для эмуляции браузерного JavaScript API.
Для того, чтобы полностью понять когда будут выполняться функции таймера, имеет смысл
прочитать об Event Loop в Node.js.

API Node.js предоставляет несколько способов планирования кода, который нужно
выполнить, в какой-то момент в будущем. Приведенные ниже функции могут показатья знакомыми, так как
они доступны в большинстве браузеров, но Node.js на самом деле предоставляет
свою реализацию этих методов. Таймеры очень тесно интегрируются с системой, и, несмотря на то,
что API Node.js отражает API браузера, все равно имеются некоторые различия в реализации.

setTimeout() может использоваться для планирования выполнения кода после назначенного
количества миллисекунд. Эта функция аналогична window. setTimeout() из JavaScript API браузера, однако строка кода не может передаваться
в качестве аргумента для выполнения.

setTimeout() первым параметром принимает функцию, которую нужно выполнить, и задержку в миллисекундах,
как число, в качестве второго параметра. Также можно перечислить дополнительные аргументы и они
будут переданы функции. Вот пример этого:

function myFunc(arg) {
  console.log(`arg was => ${arg}`);
}

setTimeout(myFunc, 1500, 'funky');

Функция myFunc() выполнится через время, максимально приближенное к
1500 миллисекундам (или 1.5 секунды), из-за вызова setTimeout().

Нельзя полагаться на то, что тайм-аут выполнится после этого точного количества миллисекунд.
Это связано с тем, что другой исполняемый код, который блокирует или удерживает цикл событий,
отодвигает выполнение тайм-аута на задний план. Единственной гарантией является то, что
тайм-аут не будет выполнен раньше, чем заданный интервал.

setTimeout() возвращает объект Timeout, который можно использовать в качестве ссылки
на тайм-аут, который был установлен. Этот объект можно использовать для отмены тайм-аута (см. clearTimeout() ниже), а также для изменения поведения при выполнении (см. unref() ниже).

setImmediate() выполнит код в конце текущего цикла событий.
Этот код будет выполняться после любых операций ввода-вывода в текущем цикле событий и
перед любым запланированными таймерами для следующего цикла событий. Такое выполнение кода
можно рассматривать как «сразу после этого», то есть любой код, следующий за вызовом
функции setImmediate(), будет выполняться до аргумента функции setImmediate().

Первым аргументом setImmediate() будет функция, которую нужно выполнить. Все последующие
аргументы будут переданы функции при ее выполнении. Вот пример:

console.log('before immediate');

setImmediate((arg) => {
  console. log(`executing immediate: ${arg}`);
}, 'so immediate');

console.log('after immediate');

Функция, переданная в setImmediate(), будет выполнена после того,
как будет выполнен весь исполняемый код, и в консоли мы увидим следующее:

before immediate
after immediate
executing immediate: so immediate

setImmediate() возвращает объект Immediate, который можно использовать для отмены
запланированного immediate (см. clearImmediate() ниже).

Примечание: Не путайте setImmediate() и process.nextTick(). Между ними есть
несколько основных различий. Во-первых, process.nextTick() выполнится перед любыми Immediate,
а также перед любыми запланированными операциями ввода/вывода. Во-вторых, process.nextTick() не подлежит
отмене, имеется в виду, что после того как вы запланировали выполнение кода с помощью process.nextTick(),
то его выполнение не может быть приостановлено, также как и с обычной функцией. Обратитесь к
этому руководству, чтобы лучше понять
работу process.nextTick().

Если у вас есть код, который нужно выполнить несколько раз, то можно использовать
для этого setInterval(). setInterval() принимает параметром функцию, которая будет
выполняться бесконечное количество раз с заданным интервалом в миллисекундах, сам интервал передается
вторым параметром. Как и в случае с setTimeout() можно передавать дополнительные аргументы,
эти аргументы будут переданы функции при вызове. Также как и с setTimeout(), задержка не может
быть гарантирована из-за операций, которые могут удерживать цикл событий, следовательно, нужно
рассматривать эту задержку как приблизительную. Смотрите пример ниже:

function intervalFunc() {
  console.log('Cant stop me now!');
}

setInterval(intervalFunc, 1500);

В примере выше intervalFunc() будет выполняться каждые 1500 миллисекунд
или 1.5 секунд, до тех пор, пока ее не остановят (см. ниже).

setInterval(), также как и setTimeout() возвращает объект Timeout, который
можно использовать в качестве ссылки для изменения установленного интервала.

Отмена грядущего

Что нужно сделать, чтобы отменить Timeout или Immediate? setTimeout(), setImmediate() и
setInterval() возвращают объект таймера, который можно использовать в качестве ссылки на установленные
Timeout и Immediate объекты. При передаче этого объекта в соответствующую функцию clear — выполнение
этого объекта будет полностью остановлено. clearTimeout(), clearImmediate() и clearInterval() — это те
самые специальные функции. Давайте посмотрим на пример ниже:

const timeoutObj = setTimeout(() => {
  console.log('timeout beyond time');
}, 1500);

const immediateObj = setImmediate(() => {
  console.log('immediately executing immediate');
});

const intervalObj = setInterval(() => {
  console. log('interviewing the interval');
}, 500);

clearTimeout(timeoutObj);
clearImmediate(immediateObj);
clearInterval(intervalObj);

Оставляя тайм-ауты позади

Помните, что setTimeout и setInterval возвращают объект Timeout.
У объекта Timeout есть два метода, чтобы расширить свое поведение, это
unref() и ref(). Если есть объект Timeout, запланированный с использованием функции set,
то для этого объекта может быть вызвана unref(). Это немного изменит поведение тем, что объект
Timeout не выполнит запланированный код, если это последний код, который нужно выполнить. Объект Timeout
не будет удерживать процесс в ожидании выполнения.

Аналогичным образом, объект Timeout, на котором был вызван unref(),
может убрать это поведение, вызвав ref() на том же Timeout объекте, что затем
обеспечит его выполнение. Однако имейте в виду, что это не точно восстанавливает
исходное поведение по причинам производительности. Давайте взглянем на примеры ниже:

const timerObj = setTimeout(() => {
  console.log('will i run?');
});




timerObj.unref();


setImmediate(() => {
  timerObj.ref();
});

Далее по событийному циклу

В событийном цикле и таймерах можно найти гораздо больше информации, чем в
этом руководстве. Чтобы узнать больше о внутренностях цикла событий Node.js и о том,
как работают таймеры во время выполнения — взгляните на это руководство: The Node.js Event Loop, Timers, and process.nextTick().

setTimeout и setInterval | Прочее (Примеры)

Поскольку JavaScript поддерживает асинхронность, есть возможность запланировать выполнение функции, используя функции setTimeout и setInterval.

Замечание: Таймауты не являются частью стандарта ECMAScript, они были разработаны как раздел спецификации DOM.

function foo() {}
var id = setTimeout(foo, 1000) // возвращает число > 0

Функция setTimeout возвращает идентификатор таймаута и планирует вызвать foo через, примерно, тысячу миллисекунд. Функция foo при этом будет вызвана ровно один раз.

В зависимости от разрешения таймера в используемом для запуска кода движке JavaScript, а также с учётом того, что JavaScript является однопоточным языком и посторонний код может заблокировать выполнение потока, нет никакой гарантии, что переданный код будет выполнен ровно через указанное в вызове setTimeout время.

Переданная первым параметром функция будет вызвана как глобальный объект — это значит, что оператор this в вызываемой функции будет ссылаться на этот самый объект.

function Foo() {
  this.value = 42
  this.method = function () {
    // this ссылается на глобальный объект
    console. log(this.value) // выведет в лог undefined
  }
  setTimeout(this.method, 500)
}
new Foo()

Замечание: Поскольку setTimeout принимает объект функции в качестве первого параметра, часто совершается ошибка в использовании setTimeout(foo(), 1000), при котором будет использоваться возвращённое значение от вызова функции foo, а не вызываться сама функция foo. В большинстве случаев ошибка пройдёт незамеченной, а в случае если функция возвращает undefined, setTimeout вообще не породит никакой ошибки.

Поочерёдные вызовы с использованием

setInterval

setTimeout вызывает функцию единожды; setInterval — как и предполагает название — вызывает функцию каждые X миллисекунд. И его использование не рекомендуется.

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

function foo() {
  // что-то, что выполняется одну секунду
}
setInterval(foo, 100)

В приведённом коде foo выполнится один раз и заблокирует этим главный поток на одну секунду.

Пока foo блокирует код, setInterval продолжает планировать последующие её вызовы. Теперь, когда первая foo закончила выполнение, в очереди будут уже десять ожидающих выполнения вызовов foo.

Разбираемся с потенциальной блокировкой кода

Самый простой и контролируемый способ — использовать setTimeout внутри самой функции.

function foo() {
  // что-то, выполняющееся одну секунду
  setTimeout(foo, 100)
}
foo()

Такой способ не только инкапсулирует вызов setTimeout, но и предотвращает от очередей блокирующих вызовов и при этом обеспечивает дополнительный контроль. Сама функция foo теперь принимает решение, хочет ли она запускаться ещё раз или нет.

Очистка таймаутов вручную

Удаление таймаутов и интервалов работает через передачу соответствующего идентификатора либо в функцию clearTimeout, либо в функцию clearInterval — в зависимости от того, какая функция set... использовалась для его получения.

var id = setTimeout(foo, 1000)
clearTimeout(id)

Очистка всех таймаутов

Из-за того, что встроенного метода для удаления всех таймаутов и/или интервалов не существует, для достижения этой цели приходится использовать брутфорс.

// удаляем "все" таймауты
for (var i = 1; i < 1000; i++) {
  clearTimeout(i)
}

Вполне могут остаться таймауты, которые не будут захвачены этим произвольным числом; так что всё же рекомендуется следить за идентификаторами всех создающихся таймаутов, за счёт чего их можно будет удалять индивидуально.

Скрытое использование

eval

setTimeout и setInterval могут принимать строку в качестве первого параметра. Эту возможность не следует использовать никогда, поскольку изнутри при этом производится скрытый вызов eval.

Замечание: Поскольку функции работы с таймаутами не определены в стандарте ECMAScript, точная внутренняя механика их работы может различаться от движка к движку. Известно, что Microsoft JScript использует конструктор Function вместо eval.

function foo() {
  // будет вызвана
}

function bar() {
  function foo() {
    // никогда не будет вызывана
  }
  setTimeout('foo()', 1000)
}
bar()

Поскольку eval в этом случае не вызывается напрямую, переданная в setTimeout строка будет выполнена в глобальной области видимости; так что локальная переменная foo из области видимости bar не будет выполнена.

По этим же причинам рекомендуется не использовать строку для передачи аргументов в функцию, которая должна быть вызвана из одной из двух функций, работающих с таймаутами.

function foo(a, b, c) {}

// НИКОГДА не делайте такого
setTimeout('foo(1,2, 3)', 1000)

// Вместо этого используйте анонимную функцию
setTimeout(function () {
  foo(1, 2, 3)
}, 1000)

Замечание: При том, что синтаксис setTimeout(foo, 1000, 1, 2, 3) разрешено использовать, это крайне не рекомендуется, поскольку может привести к сложно распознаваемым ошибкам при работе с методами.

Заключение

Никогда не используйте строки как параметры setTimeout или setInterval. Это явный признак действительно плохого кода. Если вызываемой функции необходимо передавать аргументы, лучше передавать анонимную функцию, которая самостоятельно будет отвечать за сам вызов.

Кроме того, избегайте использования setInterval в случаях, когда его планировщик может блокировать выполнение JavaScript.

Методы setTimeout и setInterval в Javascript

Видеоурок к статье


 

Введение

Для того, чтобы понять чем отличается метод setTimeout от setInterval давайте на примерах разберем, как работают эти методы.

 

 

Метод setTimeout

Метод setTimeout служит для того, чтобы вы смогли выполнить какой-нибудь Javascript сценарий с определенным интервалом.

 

Пример setTimeout:

Предположим, что вам нужно вывести модальное окно или какой-нибудь текст не сразу после загрузки страницы, а после того, как пользователь провел на вашей странице 10 секунд. 

 

Создайте html файл с содержимым:

 

В данном примере, мы создали структуру документа html, создали div с id settimeout и подключили библиотеку JQuery.  

Затем мы создали анонимный метод setTimeout и задали время выполнения через 10 секунд. Обратите внимание, что интервал задается в миллисекундах.

Внутри метода мы говорим, что хотим в div с id settimeout вставить html с текстом «Я появился через 10 секунд после загрузки страницы».

Стоит заметить, что setTimeout выполняется только один раз. 

 

Метод setInterval

В отличии от setTimeout, метод setInterval выполняется до тех пор, пока не будет вызван метод clearInterval

Для демонстрации метода setInterval, усложним немного пример. 

Наш пример будет состоять из двух  файлом ajax.php в котором мы будем хранить массив и возвращать его в формате json.

В файле index.html мы будем ajax’сом выводить новости из файла ajax.php с интервалом две секунды.

 

Файл ajax.php

 

В этом файле для примера мы будем хранить данные новостей. Первой строкой мы указываем кодировку utf-8 чтобы можно было корректно отображать русский язык. Далее мы создаем многомерный массив. И строкой json_encode мы преобразуем массив php в формат json для работы с этими данными в javascript.

 

Теперь дополните созданный файл из прошлого примера index.html следующим кодом:

 

Методом библиотеки Jquery $.ajax мы делаем фоновую загрузку данных из файла ajax.php. Далее мы создаем функцию setInterval где итеративно добавляем к переменной count единицу. Методом setInterval мы заменили цикл each, чтобы продемонстрировать setInterval.

Методом Jquery append добавляем к div c id setinterval данные. Когда новости заканчиваются, мы очищаем метод setInterval clearInterval, для того, чтобы он больше не выполнялся.  

В результате вы увидите, что новости выводятся не все сразу, а с интервалом две секунды.

 

Выводы

Методы setInterval и setTimeout используются очень часто в работе веб-программиста. 

Если у вас остались вопросы и непонятен принцип работы этих двух полезных методов javascript, посмотрите видеоурок статье.

Скачать примеры вы можете по этой ссылке. 

Подписывайтесь на наш канал Youtube, вступайте в группу VK. Также вы можете подписаться на нашу почтовую рассылку, чтобы быть в курсе о новых и полезных материалах для веб-разработчика.



Читайте также


Все материалы с сайта wh-db.com и ru.wh-db.com защищены авторским правом. Копирование, публикация, продажа и распространение материала строго запрещены.

таймеров в Node.js | Node.js

Редактировать на GitHub

Модуль таймеров в Node.js содержит функции, которые выполняют код после набора
период времени. Таймеры не нужно импортировать через require () , поскольку
все методы доступны во всем мире для эмуляции JavaScript API браузера.
Чтобы полностью понять, когда будут выполняться функции таймера, рекомендуется
читайте на Node. js
Цикл событий.

API Node.js предоставляет несколько способов планирования выполнения кода на
какой-то момент после настоящего момента.Приведенные ниже функции могут показаться знакомыми,
поскольку они доступны в большинстве браузеров, но Node.js фактически предоставляет
собственная реализация этих методов. Таймеры очень тесно интегрируются
с системой, и несмотря на то, что API является зеркалом браузера
API, есть некоторые отличия в реализации.

setTimeout () может использоваться для планирования выполнения кода после назначенного
количество миллисекунд. Эта функция похожа на
window.setTimeout ()
из JavaScript API браузера, однако строка кода не может быть передана
быть исполненным.

setTimeout () принимает функцию для выполнения в качестве своего первого аргумента, а
миллисекундная задержка, определяемая числом в качестве второго аргумента. Дополнительный
Также могут быть включены аргументы, которые будут переданы функции. Здесь
пример этого:

  function myFunc (arg) {
  console.log (`arg was => $ {arg}`);
}

setTimeout (myFunc, 1500, «напуганный»);
  

Вышеупомянутая функция myFunc () будет выполняться как можно ближе к 1500
миллисекунды (или 1.5 секунд) по возможности за счет вызова setTimeout () .

Нельзя полагаться на то, что установленный интервал тайм-аута будет выполняться после
что точное число миллисекунд. Это потому, что другой исполняемый код,
блоки или удержания в цикле событий будут подталкивать выполнение тайм-аута
назад. Гарантия only заключается в том, что тайм-аут не выполнит раньше , чем
заявленный интервал тайм-аута.

setTimeout () возвращает объект Timeout , который можно использовать для ссылки на
установленный тайм-аут.Этот возвращенный объект можно использовать для отмены тайм-аута (
см. clearTimeout () ниже), а также изменить поведение выполнения (см.
unref () ниже).

setImmediate () выполнит код в конце текущего цикла цикла событий.
Этот код будет выполнять после любых операций ввода-вывода в текущем цикле событий и
С до — любые таймеры, запланированные для следующего цикла событий. Выполнение этого кода
можно рассматривать как происходящее «сразу после этого», то есть любой код, следующий за
вызов функции setImmediate () будет выполняться до setImmediate ()
аргумент функции.

Первым аргументом функции setImmediate () будет функция, которую нужно выполнить. Любой
последующие аргументы будут переданы функции при ее выполнении.
Вот пример:

  console.log («до немедленного»);

setImmediate ((arg) => {
  console.log (`немедленное выполнение: $ {arg}`);
}, 'так немедленно');

console.log ('сразу после');
  

Вышеупомянутая функция, переданная в setImmediate () , будет выполняться после всех запускаемых
код выполнен, и вывод консоли будет:

  до немедленного
сразу после
выполнение немедленно: так немедленно
  

setImmediate () возвращает объект Immediate , который можно использовать для отмены
запланированное немедленное (см. clearImmediate () ниже).

Не путайте setImmediate () с process.nextTick () . Есть
некоторые основные различия между ними. Во-первых, запускается process.nextTick () .
перед любые Непосредственные , которые установлены, а также перед любым запланированным вводом-выводом.
Во-вторых, process.nextTick () не очищается, то есть один раз
код был запланирован для выполнения с process.nextTick () , выполнение
не может быть остановлен, как и при нормальной работе.Обратитесь к этому руководству
чтобы лучше понять работу process.nextTick () .

Если есть блок кода, который должен выполняться несколько раз, setInterval ()
можно использовать для выполнения этого кода. setInterval () принимает функцию
аргумент, который будет выполняться бесконечное количество раз с заданной миллисекундой
задержка в качестве второго аргумента. Как и setTimeout () , дополнительные аргументы
могут быть добавлены после задержки, и они будут переданы в вызов функции.Также, как и setTimeout () , задержка не может быть гарантирована из-за операций
которые могут удерживать цикл событий и поэтому должны рассматриваться как
примерная задержка. См. Пример ниже:

  function intervalFunc () {
  console.log («Не могу меня остановить!»);
}

setInterval (intervalFunc, 1500);
  

В приведенном выше примере intervalFunc () будет выполняться примерно каждые 1500
миллисекунды или 1,5 секунды, пока он не будет остановлен (см. ниже).

Как и setTimeout () , setInterval () также возвращает объект Timeout , который
может использоваться для ссылки и изменения установленного интервала.

Что можно сделать, если необходимо отменить объект Timeout или Immediate ?
setTimeout () , setImmediate () и setInterval () возвращают объект таймера
который может использоваться для ссылки на набор Timeout или Immediate object.
Путем передачи указанного объекта в соответствующую функцию clear выполнение
этот объект будет полностью остановлен. Соответствующие функции:
clearTimeout () , clearImmediate () и clearInterval () .См. Пример
ниже пример каждого:

  const timeoutObj = setTimeout (() => {
  console.log ('тайм-аут вне времени');
}, 1500);

constmediateObj = setImmediate (() => {
  console.log («немедленное выполнение»);
});

const intervalObj = setInterval (() => {
  console.log ('опрос интервала');
}, 500);

clearTimeout (timeoutObj);
clearImmediate (непосредственныйObj);
clearInterval (intervalObj);
  

Помните, что Timeout объектов возвращаются setTimeout и setInterval .Объект Timeout предоставляет две функции, предназначенные для увеличения Timeout
поведение с unref () и ref () . Если запланирован объект Timeout
используя функцию set , для этого объекта можно вызвать unref () . Это изменится
поведение немного, и не вызывать объект Timeout , если он последний
код для выполнения
. Объект Timeout не поддерживает процесс, ожидая
выполнить.

Аналогичным образом объект Timeout , для которого было вызвано unref ()
можно удалить это поведение, вызвав ref () для того же объекта Timeout ,
который затем обеспечит его выполнение. Однако имейте в виду, что это
не точно, восстанавливает исходное поведение по соображениям производительности. Видеть
ниже примеры обоих:

  const timerObj = setTimeout (() => {
  console.log ('я буду работать?');
});




timerObj.unref ();



setImmediate (() => {
  timerObj.ref ();
});
  

Цикл событий и таймеры — это гораздо больше, чем это руководство
покрыл. Чтобы узнать больше о внутреннем устройстве Node.js
Цикл событий и то, как таймеры работают во время выполнения, проверьте
это руководство по Node.js: Цикл событий Node.js, таймеры и
process.nextTick ().

Таймеры Discover JavaScript

setTimeout ()

При написании кода JavaScript вы можете захотеть отложить выполнение функции.

Это задание setTimeout . Вы указываете функцию обратного вызова, которая будет выполняться позже, и значение, выражающее, как позже вы хотите, чтобы она запускалась, в миллисекундах:

  setTimeout (() => {
  
}, 2000)

setTimeout (() => {
  
}, 50)  

Этот синтаксис определяет новую функцию. Вы можете вызвать любую другую функцию, которую хотите, или вы можете передать имя существующей функции и набор параметров:

  const myFunction = (firstParam, secondParam) => {
  
}


setTimeout (myFunction, 2000, firstParam, secondParam)  

setTimeout возвращает идентификатор таймера.Обычно это не используется, но вы можете сохранить этот идентификатор и сбросить его, если хотите удалить это запланированное выполнение функции:

  const id = setTimeout (() => {
  
}, 2000)


clearTimeout (идентификатор)  

Нулевая задержка

Если вы укажете время ожидания на 0 , функция обратного вызова будет выполнена как можно скорее, но после выполнения текущей функции:

  setTimeout (() => {
  console.log ('после')
}, 0)

console.log ('до')  

напечатает до .

Это особенно полезно, чтобы избежать блокировки ЦП при выполнении интенсивных задач и позволить другим функциям выполняться при выполнении тяжелых вычислений с помощью функций очереди в планировщике.

Некоторые браузеры (IE и Edge) реализуют метод setImmediate () , который выполняет те же самые функции, но он не является стандартным и недоступен в других браузерах. Но это стандартная функция в Node.js.

setInterval ()

setInterval — это функция, аналогичная setTimeout , с той разницей, что вместо однократного запуска функции обратного вызова она будет запускаться вечно с указанным вами интервалом времени (в миллисекундах):

  setInterval (() => {
  
}, 2000)  

Вышеупомянутая функция запускается каждые 2 секунды, если вы не прикажете ей остановиться, используя clearInterval , передав ему идентификатор интервала, который вернул setInterval :

  const id = setInterval (() => {
  
}, 2000)

clearInterval (идентификатор)  

Обычно вызывается clearInterval внутри функции обратного вызова setInterval, чтобы позволить ей автоматически определять, следует ли ей запускать снова или останавливаться.Например, этот код запускает что-то, если App.somethingIWait не имеет значение , прибыло :

  const interval = setInterval (() => {
  if (App.somethingIWait === 'прибыл') {
    clearInterval (интервал)
    возвращаться
  }
  
}, 100)  

Рекурсивный setTimeout

setInterval запускает функцию каждые n миллисекунд, не обращая внимания на то, когда функция завершила свое выполнение.

Если функция всегда занимает одинаковое количество времени, все в порядке:

Может быть, функция занимает разное время выполнения, в зависимости от условий сети, например:

И, возможно, одно длинное выполнение перекрывает следующее:

Чтобы избежать этого, вы можете запланировать рекурсивный вызов setTimeout после завершения функции обратного вызова:

  const myFunction = () => {
  

  setTimeout (myFunction, 1000)
}

setTimeout (myFunction, 1000)  

для реализации этого сценария:

setTimeout и setInterval доступны в Node.js через модуль Таймеры.

Node.js также предоставляет setImmediate () , что эквивалентно использованию setTimeout (() => {}, 0) , в основном используется для работы с циклом событий Node.js.

set-interval-async — npm

Современная версия setInterval для обещаний и асинхронных функций, доступных в Node.js и браузерах.

setIntervalAsync работает как на Node.js, так и в браузере, предоставляя тот же интерфейс
, что и setInterval для асинхронных функций, при этом предотвращая перекрытие нескольких выполнений
во времени.

Node.js

Вы можете установить setIntervalAsync , используя npm:

 npm установить -E установить интервал-async 

Или используя пряжу:

 пряжа добавить -E set-interval-async 

Теперь вам может потребоваться setIntervalAsync в CommonJS:

 // Выберите один из следующих вариантов: динамический, фиксированный, устаревший.

const {setIntervalAsync} = require ('set-interval-async / dynamic')
const {setIntervalAsync} = require ('set-interval-async / fixed')
const {setIntervalAsync} = require ('set-interval-async / legacy')
const {clearIntervalAsync} = require ('set-interval-async')

// Или потребовать все сразу:

const {
  динамический: {setIntervalAsync: setIntervalAsyncD},
  исправлено: {setIntervalAsync: setIntervalAsyncF},
  наследие: {setIntervalAsync: setIntervalAsyncL},
  clearIntervalAsync
} = require ('set-interval-async') 

Или вы можете использовать синтаксис модулей ES6:

 // Выберите один из следующих вариантов: динамический, фиксированный, устаревший.импортировать {setIntervalAsync} из 'set-interval-async / dynamic'
импортировать {setIntervalAsync} из 'set-interval-async / fixed'
импортировать {setIntervalAsync} из 'set-interval-async / legacy'
импортировать {clearIntervalAsync} из 'set-interval-async'

// Импортируем все сразу:

импорт {
  динамический
  фиксированный,
  наследие,
  clearIntervalAsync
} из 'set-interval-async'
const {setIntervalAsync: setIntervalAsyncD} = динамический
const {setIntervalAsync: setIntervalAsyncF} = фиксированный
const {setIntervalAsync: setIntervalAsyncL} = устаревший 

Браузер

В браузере вы можете добавить тег скрипта в свой HTML:

  

После загрузки скрипта в глобальном контексте будет определен модуль SetIntervalAsync .
Теперь вы можете получить функцию setIntervalAsync в любом из ее вариантов:

 // Выберите один из следующих вариантов: динамический, фиксированный, устаревший.

вар setIntervalAsync = SetIntervalAsync.dynamic.setIntervalAsync
вар setIntervalAsync = SetIntervalAsync.fixed.setIntervalAsync
var setIntervalAsync = SetIntervalAsync.legacy.setIntervalAsync

// Также загружаем `clearIntervalAsync`.

var clearIntervalAsync = SetIntervalAsync.clearIntervalAsync 

В самом базовом сценарии вы можете использовать setIntervalAsync так же, как и vanilla setInterval . Например, такой код:

 const {
  setIntervalAsync,
  clearIntervalAsync
} = требуется ('установить интервал асинхронный / динамический')

setIntervalAsync (
  () => console.log ('Привет'),
  1000
) 

будет печатать «Hello» на консоли раз в секунду.Однако вы также можете предоставить асинхронную функцию (или функцию, возвращающую обещание):

 постоянный таймер = setIntervalAsync (
  async () => {
    console.log ('Привет')
    ждать doSomeWork ()
    console.log ('Пока')
  },
  1000
)

// или эквивалентно

константный таймер = setIntervalAsync (
  () => {
    console.log ('Привет')
    вернуть doSomeWork (). затем (
      () => console.log ('Пока')
    )
  },
  1000
) 

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

 it ('должен что-то проверить', async () => {
  константный таймер = setIntervalAsync (
    / * некоторая асинхронная функция * /,
    / * некоторый интервал * /
  )
  // Сделаем некоторые утверждения.
  ожидание clearIntervalAsync (таймер)
  // На этом этапе все таймеры очищены, а последний
  // выполнение также гарантированно завершится.}) 

Где setIntervalAsync действительно хорош в тех ситуациях, когда данная асинхронная функция может занимать больше времени для вычисления, чем настроенный интервал, и, в то же время, небезопасно выполнять более одного раза за раз. Использование vanilla setInterval нарушит ваш код в этом сценарии, тогда как setIntervalAsync гарантирует, что функция никогда не будет выполняться более одного раза в одно и то же время. Например, рассмотрите:

 async function processQueue (queue) {
  если (очередь.length === 0) {
    возвращаться
  }
  let head = queue [0]
  жду doSomeWork (голова)
  queue.shift () // Удаляет первый элемент.
} 

Вышеуказанная функция никогда не должна вызываться снова до завершения предыдущего выполнения. В противном случае первый элемент очереди будет обработан дважды, а второй элемент будет пропущен. Однако с setIntervalAsync совершенно безопасно следующее:

 setIntervalAsync (processQueue, 1000, очередь) 

с setIntervalAsync гарантирует, что функция никогда не будет выполняться более одного раза в любой момент.Вы можете выбрать, хотите ли вы использовать варианты Dynamic или Fixed , которые либо запускают каждое выполнение как можно скорее, либо устанавливают фиксированную задержку между концом одного выполнения и началом следующего. См. Динамический и фиксированный setIntervalAsync для получения дополнительных сведений.

Чтобы увидеть полный набор примеров и инструкции по их запуску, посетите наш каталог примеров.

Если вам когда-либо приходилось иметь дело со странными, незаметными ошибками в результате использования setInterval [1] в асинхронных функциях, или вам приходилось вручную переопределить setInterval с помощью setTimeout [2], чтобы предотвратить многократное выполнение одна и та же асинхронная функция от перекрытия, то эта библиотека станет заменой, которая решит ваши проблемы.

setInterval выполняет заданную функцию многократно, один раз в фиксированное количество миллисекунд. Это может вызвать проблемы всякий раз, когда выполнение функции занимает больше времени, чем заданный интервал, поскольку она будет вызываться снова до завершения первого выполнения. Это часто проблема для функций без повторного входа; т.е. функции, которые не предназначены для одновременного выполнения нескольких операций.

setIntervalAsync — это прямая замена setInterval , который использует тот же API, но безопасен для использования с асинхронными функциями без повторного входа.

[1] https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout
[2] https://developer.mozilla.org/en-US/docs/Web/API / WindowOrWorkerGlobalScope / setInterval

setIntervalAsync предоставляет две стратегии, которые можно использовать для предотвращения выполнения повторяющейся функции более одного раза в любой данный момент:

  • Dynamic : Если возможно, данная функция вызывается один раз каждые интервала миллисекунды.Если какое-либо выполнение занимает больше времени, чем желаемый интервал, следующее выполнение откладывается до завершения предыдущего и вызывается сразу после достижения этого условия.

  • Фиксированный : Данная функция вызывается повторно, что гарантирует фиксированную задержку в интервала миллисекунды между концом одного выполнения и началом следующего.

Вы можете выбрать ту стратегию, которая лучше всего подходит для вашего приложения.В случае сомнений, стратегия Dynamic , вероятно, будет достаточной для большинства случаев использования, сохраняя интервал как можно ближе к желаемому.

Вы можете просмотреть полный API на нашей странице документации.

Чтобы внести свой вклад в этот проект, вам необходимо сначала клонировать репозиторий:

 git clone https://github.com/ealmansi/set-interval-async.git 

Убедитесь, что Yarn глобально установлен в вашей системе,
установите все зависимости проекта и соберите проект:

Теперь вы можете запустить тесты и убедиться, что все работает правильно:

Если предыдущий шаг завершился успешно, вы готовы начать разработку этого проекта.
Pull-запросы приветствуются!

Вы можете убедиться, что ваш код соответствует стандартному стилю JavaScript, с помощью следующей команды:

MIT

Неожиданная остановка функции обратного вызова setInterval

· Проблема № 22149 · nodejs / node · GitHub

@apapirovski
С фоновым таймером я имею в виду, что это в основном функция, которая выполняет некоторую задачу, а затем перепланировывает себя с помощью setTimeout (). Без unref (), только setTimeout и иногда clearTimeout

Как это:

 function doSomething () {
    PromiseReturningFunction ().затем ((данные) => {
        //сделать некоторые
    }). catch ((error) => {
        // регистрируем ошибку
    }). then (() => {
        setTimeout (doSomething, 5000);
    });
} 

Я вызываю эту функцию один раз, когда приложение поднимается, а затем они должны продолжать работать, не ткнувшись извне. Отсюда «backgrund task», но я никогда не использую ничего, кроме setTimeout, для планирования.

Например, таймер api устанавливается в ответ на вызов api. Например, у меня есть вызов, который включает брандмауэр, который устанавливает таймер для автоматического отключения, если пользователь не подтвердит действие в течение 30 секунд.Этот таймер не работает. Соответствующий код выглядит следующим образом;

 enableFirewall: (параметры) => {
        return getFwStatus (). then ((до) => {
            return execp ('sudo ufw --force enable'). then (() => {
                setDisableTimer (disableTime);
            }). then (() => {
                return getFwStatus (). then ((после) => {
                    options.before = before;
                    options.after = after;
                    return addAuditLogEntry (параметры);
                });
            });
        }).тогда (() => {
            return disableTime;
        }). catch ((error) => {
            options.name = 'ошибка брандмауэра';
            return disableFirewall (options) .catch (() => {
                вернуть execp ('sudo ufw disable')
            }). then (() => {
                return Promise.reject (ошибка);
            });
        });
    }

function setDisableTimer (timeout) {
    cancelDisableTimer ();
    disableFirewallTimer = setTimeout (() => {
        disableFirewall ({пользователь: '- отключить таймер--'});
    }, тайм-аут);
}

function cancelDisableTimer () {
    if (disableFirewallTimer! = null) {
        clearTimeout (disableFirewallTimer);
        disableFirewallTimer = ноль;
    }
}

function disableFirewall (options) {
    var options = options || {user: '--unknown user--'}
    вернуть getFwStatus ().then ((до) => {
        return execp ('sudo ufw disable'). then (() => {
            return getFwStatus (). then ((после) => {
                options.before = before;
                options.after = after;
                return addAuditLogEntry (параметры);
            });
        });
    });
} 

Вызов api вызовет enableFirewall, и я действительно вижу при пошаговом выполнении кода, который он достигает и выполняет без ошибки

 disableFirewallTimer = setTimeout (() => {
        disableFirewall ({пользователь: '- отключить таймер--'});
    }, тайм-аут); 

и что параметр тайм-аута действительно установлен на 30000, как я и ожидал.Но точка останова в анонимной функции никогда не срабатывает (и брандмауэр также не вызывается).

Одна из «фоновых задач», которая больше не работает

 function keepNtpStatusFresh () {
    getNtpStatusOutputInternal (). then ((data) => {
        lastNtpCheck.data = данные;
        lastNtpCheck.when = новая дата ();
    }). catch ((error) => {
        lastNtpCheck.data = ноль;
        sails.log.error (ошибка);
    }). then (() => {
        setTimeout (keepNtpStatusFresh, 5000);
    });
}

function getNtpStatusOutputInternal () {
    return execp (sails.\ s =] +) \ s + (\ S +) \ s + (\ S +) \ s + (\ S +) \ s + (\ S +) \ s + (\ S +) \ s + (\ S +) \ s + (\ S +) \ s + (\ S +) \ s + (\ S +) / gm);
        var match = re.exec (результат.stdout);

        вар серверов = [];
        while (match! = null) {
            server.push ({
                тип: совпадение [1],
                удаленный: совпадение [2],
                refid: match [3],
                st: Число (совпадение [4]),
                t: совпадение [5],
                когда: Число (соответствует [6]),
                опрос: Число (совпадение [7]),
                охват: Число (совпадение [8]),
                задержка: Число (совпадение [9]),
                смещение: Число (совпадение [10]),
                джиттер: Число (совпадение [11]),
            });
            match = re.exec (результат.stdout);
        }
        return {output: result.stdout, serverList: servers};
    });
} 

Кооперативный асинхронный JavaScript: таймауты и интервалы — Изучите веб-разработку

В этом руководстве рассматриваются традиционные методы, доступные в JavaScript для асинхронного выполнения кода по истечении заданного периода времени или через регулярный интервал (например, заданное количество раз в секунду), обсуждается, для чего они полезны, и рассматриваются их присущие вопросы.

Предварительные требования: Базовая компьютерная грамотность, разумное понимание основ JavaScript.
Цель: Чтобы понять асинхронные циклы и интервалы и для чего они нужны.

В течение долгого времени веб-платформа предлагала программистам JavaScript ряд функций, которые позволяют им асинхронно выполнять код по истечении определенного временного интервала, а также повторно выполнять блок кода асинхронно, пока вы не скажете, чтобы он остановился. .

Эти функции:

setTimeout ()
Выполняет указанный блок кода один раз по истечении указанного времени.
setInterval ()
Повторно выполнять указанный блок кода с фиксированной временной задержкой между каждым вызовом.
requestAnimationFrame ()
Современная версия setInterval () . Выполняет указанный блок кода перед тем, как браузер в следующий раз перерисовывает отображение, позволяя запускать анимацию с подходящей частотой кадров независимо от среды, в которой она выполняется.

Асинхронный код, установленный этими функциями, выполняется в основном потоке (по истечении указанного таймера).

Важно знать, что вы можете (и часто будете) запускать другой код до выполнения вызова setTimeout () или между итерациями setInterval () . В зависимости от того, насколько интенсивно используются эти операции для процессора, они могут еще больше задержать выполнение асинхронного кода, поскольку любой асинхронный код будет выполняться только после после того, как станет доступным для основного потока.(Другими словами, когда стек пуст.) Вы узнаете больше по этому вопросу по мере прохождения этой статьи.

В любом случае эти функции используются для запуска постоянной анимации и другой фоновой обработки на веб-сайте или в приложении. В следующих разделах мы покажем вам, как их можно использовать.

Как мы уже говорили, setTimeout () выполняет определенный блок кода один раз по истечении заданного времени. Принимает следующие параметры:

  • Функция для запуска или ссылка на функцию, определенную в другом месте.
  • Число, представляющее интервал времени в миллисекундах (1000 миллисекунд равняется 1 секунде) ожидания перед выполнением кода. Если вы укажете значение 0 (или опустите значение), функция запустится при первой возможности. (См. Примечание ниже о том, почему он запускается «как можно скорее», а не «немедленно».) Подробнее о том, почему вы, возможно, захотите сделать это позже.
  • Ноль или более значений, представляющих любые параметры, которые вы хотите передать функции при ее запуске.

ПРИМЕЧАНИЕ: Указанное количество времени (или задержка) составляет , а не гарантированное время до выполнения, а минимальное время до выполнения.Обратные вызовы, которые вы передаете этим функциям, не могут выполняться, пока стек в основном потоке не станет пустым.

Как следствие, такой код, как setTimeout (fn, 0) , будет выполняться, как только стек станет пустым, а не сразу . Если вы выполните код вроде setTimeout (fn, 0) , но сразу после запуска цикла, который насчитывает от 1 до 10 миллиардов, ваш обратный вызов будет выполнен через несколько секунд.

В следующем примере браузер будет ждать две секунды перед выполнением анонимной функции, затем отобразит предупреждающее сообщение (посмотрите, как оно работает, и посмотрите исходный код):

  let myGreeting = setTimeout (function () {
  alert ('Здравствуйте, мистерВселенная! ');
}, 2000)  

Указанные вами функции не обязательно должны быть анонимными. Вы можете дать своей функции имя и даже определить ее где-нибудь еще и передать ссылку на функцию в setTimeout () . Следующие две версии фрагмента кода эквивалентны первой:

 
let myGreeting = setTimeout (function sayHi () {
  alert ('Здравствуйте, мистер Вселенная!');
}, 2000)


function sayHi () {
  alert («Привет, мистер Вселенная!»);
}

let myGreeting = setTimeout (sayHi, 2000);  

Это может быть полезно, если у вас есть функция, которую нужно вызывать как по таймауту, так и в ответ на событие, например.Но это также может просто помочь сохранить ваш код в порядке, особенно если обратный вызов тайм-аута занимает больше, чем несколько строк кода.

setTimeout () возвращает значение идентификатора, которое можно использовать для ссылки на тайм-аут позже, например, когда вы хотите его остановить. См. Раздел «Очистка тайм-аутов» (ниже), чтобы узнать, как это сделать.

Передача параметров в функцию setTimeout ()

Любые параметры, которые вы хотите передать функции, выполняемой внутри setTimeout () , должны быть переданы ей в качестве дополнительных параметров в конце списка.

Например, вы можете реорганизовать предыдущую функцию, чтобы она передавала привет любому переданному ей имени:

  function sayHi (who) {
  alert (`Привет, $ {who}!`);
}  

Теперь вы можете передать имя человека в вызов setTimeout () в качестве третьего параметра:

  let myGreeting = setTimeout (sayHi, 2000, «Мистер Вселенная»);  

Очистка тайм-аутов

Наконец, если тайм-аут был создан, вы можете отменить его до истечения указанного времени, вызвав clearTimeout () , передав ему идентификатор вызова setTimeout () в качестве параметра.Итак, чтобы отменить указанный выше тайм-аут, вы должны сделать это:

  clearTimeout (myGreeting);  

Примечание : См. greeter-app.html для более сложной демонстрации, которая позволяет вам установить имя человека, с которым можно поздороваться в форме, и отменить приветствие с помощью отдельной кнопки (см. Исходный код также).

setTimeout () отлично работает, когда вам нужно запустить код один раз через установленный период времени. Но что происходит, когда вам нужно запускать код снова и снова - например, в случае анимации?

Здесь появляется setInterval () .Это работает очень похоже на setTimeout () , за исключением того, что функция, которую вы передаете в качестве первого параметра, выполняется многократно с интервалом не менее одного миллисекунды, заданного вторым параметром, а не один раз. Вы также можете передать любые параметры, необходимые для выполняемой функции, в качестве последующих параметров вызова setInterval () .

Рассмотрим пример. Следующая функция создает новый объект Date () , извлекает из него строку времени, используя toLocaleTimeString () , а затем отображает ее в пользовательском интерфейсе.Затем он запускает функцию один раз в секунду, используя setInterval () , создавая эффект цифровых часов, которые обновляются один раз в секунду (см. Это в прямом эфире, а также см. Источник):

  function displayTime () {
   пусть дата = новая дата ();
   let time = date.toLocaleTimeString ();
   document.getElementById ('демонстрация'). textContent = время;
}

const createClock = setInterval (displayTime, 1000);  

Как и setTimeout () , setInterval () возвращает идентифицирующее значение, которое вы можете использовать позже, когда вам нужно очистить интервал.

Очистка интервалов

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

  const myInterval = setInterval (myFunction, 2000);

clearInterval (myInterval);  
Активное обучение: создание собственного секундомера!

С учетом всего сказанного, у нас есть для вас вызов.Возьмите копию нашего примера setInterval-clock.html и измените его, чтобы создать свой собственный простой секундомер.

Вам нужно отображать время, как и раньше, но в этом примере вам нужно:

  • Кнопка «Пуск» для запуска секундомера.
  • Кнопка «Стоп», чтобы приостановить / остановить его.
  • Кнопка «Сброс» для сброса времени на 0 .
  • Отображение времени, показывающее количество прошедших секунд, а не фактическое время.

Вот вам несколько подсказок:

  • Вы можете структурировать и стилизовать разметку кнопок по своему усмотрению; просто убедитесь, что вы используете семантический HTML с крючками, которые позволяют захватывать ссылки на кнопки с помощью JavaScript.
  • Вероятно, вы захотите создать переменную, которая начинается с 0 , а затем увеличивается на единицу каждую секунду с использованием постоянного цикла.
  • Этот пример проще создать без использования объекта Date () , как мы это делали в нашей версии, но с меньшей точностью - вы не можете гарантировать, что обратный вызов сработает ровно через 1000 мс. Более точным способом было бы запустить startTime = Date.now () , чтобы получить точную метку времени, когда пользователь щелкнул кнопку запуска, а затем выполнить Date.now () - startTime , чтобы получить количество миллисекунд после нажатия кнопки запуска.
  • Вы также хотите вычислить количество часов, минут и секунд как отдельные значения, а затем отображать их вместе в строке после каждой итерации цикла. На втором счетчике вы можете отработать каждую из них.
  • Как бы вы их рассчитали? Подумайте об этом:
    • Количество секунд в часе 3600 .
    • Количество минут - это количество секунд, оставшееся после удаления всех часов, разделенное на 60 .
    • Количество секунд - это количество секунд, оставшееся после удаления всех минут.
  • Вы захотите включить начальный ноль в отображаемые значения, если сумма меньше 10 , чтобы они больше походили на традиционные часы / часы.
  • Чтобы приостановить секундомер, вам нужно очистить интервал. Чтобы сбросить его, вы захотите установить счетчик обратно на 0 , очистить интервал, а затем немедленно обновить отображение.
  • Вероятно, вам следует отключить кнопку запуска после ее однократного нажатия и включить ее снова после того, как вы остановили / сбросили ее. В противном случае многократное нажатие кнопки пуска применит к часам несколько setInterval () с, что приведет к неправильному поведению.

При работе с setTimeout () и setInterval () следует помнить о нескольких моментах. Давайте рассмотрим их сейчас.

Рекурсивные таймауты

Есть еще один способ использования setTimeout () : вы можете вызвать его рекурсивно для повторного выполнения одного и того же кода вместо использования setInterval () .

В приведенном ниже примере используется рекурсивная функция setTimeout () для запуска переданной функции каждые 100 миллисекунды:

  пусть i = 1;

setTimeout (function run () {
  console.log (я);
  i ++;
  setTimeout (запустить, 100);
}, 100);  

Сравните приведенный выше пример со следующим - здесь используется setInterval () для достижения того же эффекта:

  пусть i = 1;

setInterval (function run () {
  console.log (я);
  я ++
}, 100);  
Чем отличаются рекурсивные

setTimeout () и setInterval () ?

Разница между двумя версиями приведенного выше кода невелика.

  • Рекурсивный setTimeout () гарантирует такую ​​же задержку между выполнениями. (Например, 100 мс в приведенном выше случае.) Код будет запущен, затем подождет 100 миллисекунды, прежде чем он выполнится снова, поэтому интервал будет одинаковым, независимо от того, сколько времени требуется для выполнения кода.
  • Пример с использованием setInterval () работает несколько иначе. Выбранный вами интервал включает в себя время, необходимое для выполнения кода, который вы хотите запустить.Скажем, выполнение кода занимает 40 миллисекунды - тогда интервал составляет всего 60 миллисекунды.
  • При рекурсивном использовании setTimeout () каждая итерация может вычислять различную задержку перед запуском следующей итерации. Другими словами, значение второго параметра может указывать другое время в миллисекундах для ожидания перед повторным запуском кода.

Когда ваш код может занять больше времени, чем установленный вами интервал времени, лучше использовать рекурсивный setTimeout () - это сохранит постоянный временной интервал между выполнениями независимо от того, сколько времени потребуется для выполнения кода. выполнить, и вы не получите ошибок.

Немедленные тайм-ауты

Использование 0 в качестве значения для setTimeout () планирует выполнение указанной функции обратного вызова как можно скорее, но только после того, как будет запущен основной поток кода.

Например, приведенный ниже код (см. Его в реальном времени) выводит предупреждение, содержащее «Hello» , а затем предупреждение, содержащее «World» , как только вы нажимаете OK в первом предупреждении.

  setTimeout (function () {
  alert ('Мир');
}, 0);

alert ('Привет');  

Это может быть полезно в случаях, когда вы хотите настроить запуск блока кода, как только завершится выполнение всего основного потока - поместите его в цикл событий async, чтобы он запускался сразу после этого.

Очистка с помощью clearTimeout () или clearInterval ()

clearTimeout () и clearInterval () оба используют один и тот же список записей для очистки. Интересно, что это означает, что вы можете использовать любой метод для очистки setTimeout () или setInterval () .

Для согласованности следует использовать clearTimeout () для очистки записей setTimeout () и clearInterval () для очистки записей setInterval () .Это поможет избежать путаницы.

requestAnimationFrame () - это специализированная функция цикла, созданная для эффективного запуска анимации в браузере. По сути, это современная версия setInterval () - она ​​выполняет указанный блок кода перед тем, как браузер перерисовывает изображение, позволяя запускать анимацию с подходящей частотой кадров независимо от среды, в которой она выполняется.

Он был создан в ответ на предполагаемые проблемы с setInterval () , который, например, не работает с частотой кадров, оптимизированной для устройства, иногда отбрасывает кадры, продолжает работать, даже если вкладка не является активной вкладкой или анимация прокручивается со страницы и т. д.

(Подробнее об этом читайте в CreativeJS.)

Метод принимает в качестве аргумента обратный вызов, который должен быть вызван перед перерисовкой. Это общий шаблон, который вы увидите в нем:

.

  function draw () {
   
   requestAnimationFrame (рисовать);
}

рисовать();  

Идея состоит в том, чтобы определить функцию, в которой обновляется ваша анимация (например, ваши спрайты перемещаются, счет обновляется, данные обновляются и т. Д.). Затем вы вызываете его, чтобы начать процесс. В конце функционального блока вы вызываете requestAnimationFrame () со ссылкой на функцию, переданную в качестве параметра, и это дает браузеру команду вызвать функцию снова при следующей перерисовке дисплея.Затем он выполняется непрерывно, поскольку код requestAnimationFrame () рекурсивно вызывает.

Примечание : Если вы хотите выполнить какую-то простую постоянную DOM-анимацию, CSS-анимация, вероятно, будет быстрее. Они рассчитываются непосредственно внутренним кодом браузера, а не JavaScript.

Если, однако, вы делаете что-то более сложное и включаете в себя объекты, которые не доступны напрямую внутри DOM (например, 2D Canvas API или объекты WebGL), requestAnimationFrame () в большинстве случаев является лучшим вариантом.

Как быстро движется ваша анимация?

Плавность анимации напрямую зависит от частоты кадров анимации и измеряется в кадрах в секунду (fps). Чем выше это число, тем плавнее будет выглядеть ваша анимация до определенной точки.

Поскольку большинство экранов имеют частоту обновления 60 Гц, максимальная частота кадров, к которой вы можете стремиться, составляет 60 кадров в секунду (FPS) при работе с веб-браузерами. Однако большее количество кадров означает больше обработки, которая часто может вызывать заикание и пропуски, также известные как пропущенных кадров или jank .

Если у вас есть монитор с частотой обновления 60 Гц и вы хотите достичь 60 кадров в секунду, у вас есть около 16,7 миллисекунд ( 1000/60 ) на выполнение кода анимации для рендеринга каждого кадра. Это напоминание о том, что вам нужно помнить об объеме кода, который вы пытаетесь запустить во время каждого прохождения цикла анимации.

requestAnimationFrame () всегда пытается максимально приблизиться к этому магическому значению 60 FPS. Иногда это невозможно - если у вас действительно сложная анимация и вы запускаете ее на медленном компьютере, частота кадров будет меньше.Во всех случаях requestAnimationFrame () всегда будет делать все возможное с тем, что у него есть.

Чем requestAnimationFrame () отличается от setInterval () и setTimeout ()?

Давайте поговорим еще немного о том, чем метод requestAnimationFrame () отличается от других методов, используемых ранее. Глядя на наш код сверху:

  function draw () {
   
   requestAnimationFrame (рисовать);
}

рисовать();  

Давайте теперь посмотрим, как сделать то же самое с помощью setInterval () :

  function draw () {
   
}

setInterval (ничья, 17);  

Как мы уже говорили ранее, вы не указываете временной интервал для requestAnimationFrame () .Просто он работает максимально быстро и плавно в текущих условиях. Браузер также не тратит время на его запуск, если анимация по какой-то причине выходит за пределы экрана и т. Д.

setInterval () , с другой стороны, требует указания интервала . Мы пришли к нашему окончательному значению 17 по формуле 1000 миллисекунд / 60 Гц , а затем округлили его в большую сторону. Округление - хорошая идея; если вы округлите в меньшую сторону, браузер может попытаться запустить анимацию со скоростью, превышающей 60 FPS, и в любом случае это не повлияет на плавность анимации.Как мы уже говорили ранее, стандартная частота обновления - 60 Гц.

Добавление отметки времени

Фактическому обратному вызову, переданному в функцию requestAnimationFrame () , также может быть задан параметр: значение отметки времени , которое представляет время с момента запуска requestAnimationFrame () .

Это полезно, поскольку позволяет запускать вещи в определенное время и с постоянным темпом, независимо от того, насколько быстрым или медленным может быть ваше устройство. Общий шаблон, который вы бы использовали, выглядит примерно так:

  пусть startTime = null;

function draw (timestamp) {
    if (! startTime) {
      startTime = отметка времени;
    }

   currentTime = отметка времени - startTime;

   

   requestAnimationFrame (рисовать);
}

рисовать();  

Поддержка браузера

requestAnimationFrame () поддерживается в более поздних версиях браузеров, чем setInterval () / setTimeout () .Интересно, что он доступен в Internet Explorer 10 и выше.

Итак, если вам не нужна поддержка более старых версий IE, нет особых причин не использовать requestAnimationFrame () .

Простой пример

Довольно теории! Создадим ваш собственный пример requestAnimationFrame () . Вы собираетесь создать простую «анимацию прядильщика» - такую, которую вы можете увидеть в приложении, когда оно подключено к серверу и т. Д.

Примечание : В реальном примере вам, вероятно, следует использовать анимацию CSS для запуска такой простой анимации.Однако такой пример очень полезен для демонстрации использования requestAnimationFrame () , и вы с большей вероятностью воспользуетесь этой техникой при выполнении чего-то более сложного, например, обновления отображения игры в каждом кадре.

  1. Возьмите базовый шаблон HTML (например, этот).

  2. Поместите пустой элемент

    внутри , затем добавьте внутри него символ ↻. Этот символ круговой стрелки будет действовать как наш счетчик для этого примера.

  3. Примените следующий CSS к шаблону HTML (любым удобным для вас способом). Это устанавливает красный фон на странице, устанавливает высоту на 100% высоты и центрирует

    внутри по горизонтали и вертикали. .

      html {
      цвет фона: белый;
      высота: 100%;
    }
    
    тело {
      рост: наследовать;
      цвет фона: красный;
      маржа: 0;
      дисплей: гибкий;
      justify-content: center;
      align-items: center;
    }
    
    div {
      дисплей: встроенный блок;
      font-size: 10rem;
    }  
  4. Вставьте элемент .

  5. Вставьте следующий код JavaScript в элемент