setTimeout или setInterval?

763

Насколько я могу судить, эти две части JavaScript ведут себя одинаково:

Вариант А:

function myTimeoutFunction()
{
    doStuff();
    setTimeout(myTimeoutFunction, 1000);
}

myTimeoutFunction();

Вариант Б:

function myTimeoutFunction()
{
    doStuff();
}

myTimeoutFunction();
setInterval(myTimeoutFunction, 1000);

Есть ли разница между использованием setTimeout и setInterval ?

Damovisa
источник
6
Если вам нужны подробности о том, как работают таймеры в JS, Джон Резиг написал хорошую статью на эту тему
Рафаэль
9
есть также очевидное отличие в том, что setTimeout требует, чтобы для его распространения использовалась дополнительная строка кода, что имеет недостаток, заключающийся в том, что это проблема технического обслуживания, но преимущество в том, что вы можете легко изменять период
annakata
попробуйте это jsfiddle.net/pramendra/Y4vEV
Pramendra Gupta
4
Спасибо @JapanPro, но у меня никогда не было проблем с работой тайм-аутов. Этот пост был о том, в чем разница и что следует использовать.
Дамовиза

Ответы:

670

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

Хотя можно подумать, что он setIntervalбудет выполняться ровно каждые 1000 мс, важно отметить, чтоsetInterval это также задержит, поскольку JavaScript не является многопоточным языком, а это означает, что - если есть другие части выполняющегося скрипта - интервал будет иметь ждать, пока это закончится.

В этой скрипке вы можете ясно видеть, что время ожидания будет отставать, в то время как интервал почти все время составляет почти 1 вызов / секунду (что пытается сделать скрипт). Если вы измените переменную скорости в верхней части на что-то маленькое, например 20 (то есть она будет пытаться запускаться 50 раз в секунду), интервал никогда не достигнет среднего значения в 50 итераций в секунду.

Задержка почти всегда пренебрежимо мала, но если вы программируете что-то действительно точное, вам следует использовать самонастраивающийся таймер (который по сути является таймером на основе тайм-аута, который постоянно настраивается в соответствии с созданной задержкой)

Дэвид Снабель-Каунт
источник
4
У Энди было подобное предложение. Гипотетически, означает ли это, что если для выполнения метода требуется более 1000 мс, у вас может быть несколько запущенных одновременно?
Дамовиза
14
Теоретически, да. На практике нет, поскольку Javascript не поддерживает многопоточность. Если ваш код занимает больше 1000 мс, браузер заморозится.
Камиэль Ванройдж
61
Технически, код не будет выполняться ровно каждые 1000 мс, поскольку это зависит от разрешения таймера и от того, выполняется ли другой код. Ваша точка зрения все еще остается верной.
Мэтью Крамли
10
setInterval отличается двумя способами: 1. setInterval не является рекурсивным, и setInterval в первый раз вызовет вашу функцию по истечении указанного времени, тогда как setTimeout будет вызван первый раз без какой-либо задержки, и после этого он вызовет снова по истечении указанного времени. После первого исполнения они работают практически одинаково.
Хафиз
5
Разница в том, что setTimeout не повторяется. Позволяет запускать скрипт через заданное время, но только один раз. SetInterval, с другой стороны, будет повторять этот сценарий, пока он не будет остановлен с помощью clearTimeout ().
Леандер
648

Есть ли разница?

Да. Тайм-аут выполняется определенное количество времени после вызова setTimeout (); Интервал выполняется определенное количество времени после срабатывания предыдущего интервала.

Вы заметите разницу, если ваша функция doStuff () займет некоторое время для выполнения. Например, если мы представляем вызов setTimeout / setInterval с ., запуск тайм-аута / интервала с *и выполнение кода JavaScript с [-----], временные шкалы выглядят следующим образом:

Timeout:

.    *  .    *  .    *  .    *  .
     [--]    [--]    [--]    [--]

Interval:

.    *    *    *    *    *    *
     [--] [--] [--] [--] [--] [--]

Следующая сложность заключается в том, что интервал срабатывает, когда JavaScript уже занят чем-то (например, обработкой предыдущего интервала). В этом случае интервал запоминается и происходит, как только предыдущий обработчик завершает работу и возвращает управление браузеру. Так, например, для процесса doStuff (), который иногда короткий ([-]), а иногда длинный ([-----]):

.    *    *        *        *    *
     [-]  [-----][-][-----][-][-]  [-]

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

Таким образом, интервалы пытаются «наверстать упущенное», чтобы вернуться в график. Но они не стоят в очереди друг над другом: за интервал может быть только одно ожидание выполнения. (Если они все встанут в очередь, браузер останется с постоянно расширяющимся списком выдающихся казней!)

.    *            x            x
     [------][------][------][------]

x представляет интервал срабатывания, который не мог быть выполнен или ожидался, поэтому вместо этого он был отброшен.

Если ваша функция doStuff () обычно выполняется дольше, чем установленный для нее интервал, браузер потребляет 100% ресурсов ЦП, пытаясь ее обслуживать, и может стать менее отзывчивым.

Что вы используете и почему?

Chained-Timeout дает гарантированный слот свободного времени браузеру; Интервал пытается обеспечить выполнение выполняемой функции как можно ближе к запланированному времени за счет доступности пользовательского интерфейса браузера.

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

Что касается совместимости браузеров, setTimeout предшествует setInterval, но все браузеры, которые вы встретите сегодня, поддерживают оба. Последним бродягой на протяжении многих лет был IE Mobile в WinMo <6.5, но, надеюсь, это уже позади.

bobince
источник
Но действительно ли причина выбора между Interval и Timeout верна даже для не браузерных платформ, таких как, например, WebOS или JIL?
Vid L
2
Хороший способ выполнения связанных тайм-аутов для анонимных функций: setTimeout (function () {setTimeout (arguments.callee, 10)}, 10)
Джастин Мейер
1
С точки зрения совместимости браузера, хотя все браузеры предоставляют оба метода, их производительность различна.
unigg
«Но тогда есть вероятность, что он не поддерживает ничего другого, что вы используете», что очень верно
Basic
1
драм-машина по проекту хрома с использованием setTimeout ("schedule ()", 0); Так что браузер не будет иметь ненужного стека, когда это фоновая вкладка для Chrome или нехватка ресурсов.
Эллиот Яп
91

setInterval ()

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

Если вы хотите зациклить код для анимации или на такте, используйте setInterval().

function doStuff() {
    alert("run your code here when time interval is reached");
}
var myTimer = setInterval(doStuff, 5000);

SetTimeout ()

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

function doStuff() {
    alert("run your code here when time interval is reached");
}
var myTimer = setTimeout(doStuff, 5000);

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

Харис
источник
6
Хорошее объяснение, но обратите внимание, что ФП понимает основное различие в примерах, предоставленных ФП. В частности, обратите внимание, что в setTimeout()примере OP setTimeout()вызывается рекурсивно , тогда setInterval()как нет.
DavidRR
44

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

var timerId = null;
function myTimeoutFunction()
{
    doStuff();
    timerId = setTimeout(myTimeoutFunction, 1000);
}

myTimeoutFunction();

// later on...
clearTimeout(timerId);

против

function myTimeoutFunction()
{
    doStuff();
}

myTimeoutFunction();
var timerId = setInterval(myTimeoutFunction, 1000);

// later on...
clearInterval(timerId);
Камиль Ванрой
источник
35
Я не вижу, как вы не отслеживаете идентификатор таймера в случае setInterval. Это просто вышло из функции.
Кекоа
1
Другой вариант setTimeout- вместо сохранения идентификатора добавить ifоператор, чтобы установить следующий тайм-аут, только когда выполняется какое-либо условие.
nnnnnn
7
Кекоа, хорошая мысль. Но вы должны сохранить идентификатор интервала только один раз. ID тайм-аута, вероятно, меняется с каждым вызовом, поэтому вы должны отслеживать эти изменения. Код, который хочет очистить тайм-аут, должен каким-то образом иметь доступ к текущему значению этой переменной. Кроме того, невозможно очистить тайм-аут во время работы, doStuffпотому что идентификатор недействителен. Однако на самом деле нет необходимости сохранять идентификаторы тайм-аута. Вместо этого вы можете просто перестать звонить setTimeout.
Роберт
4
По моему опыту, большую часть времени вы хотите иметь возможность отменить даже при использовании setTimeout. 99% времени вы хотите отменить до следующего вызова, а не после завершения следующего.
Камиэль Ванройдж
23

Я считаю, что setTimeoutметод проще в использовании, если вы хотите отменить тайм-аут:

function myTimeoutFunction() {
   doStuff();
   if (stillrunning) {
      setTimeout(myTimeoutFunction, 1000);
   }
}

myTimeoutFunction();

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

Guffa
источник
20

Разница в их целях.

setInterval()
   -> executes a function, over and over again, at specified time intervals  

setTimeout()
   -> executes a function, once, after waiting a specified number of milliseconds

Это так просто

Более подробные детали здесь http://javascript.info/tutorial/settimeout-setinterval

kmario23
источник
7
Хорошее краткое объяснение, но обратите внимание, что ФП понимает основное различие в примерах, предоставленных ФП. В частности, обратите внимание, что в setTimeout()примере OP setTimeout()вызывается рекурсивно , тогда setInterval()как нет.
DavidRR
14

Когда вы запускаете какую-то функцию внутри setInterval, которая работает больше времени, чем timeout-> браузер застрянет.

- Например, doStuff () занимает 1500 сек. чтобы быть выполненным, и вы делаете: setInterval (doStuff, 1000);
1) Браузер запускает doStuff (), который занимает 1,5 сек. быть выполненным;
2) Через ~ 1 секунду он снова пытается запустить doStuff () . Но предыдущий doStuff () все еще выполняется ->, поэтому браузер добавляет этот запуск в очередь (запуск после первого выполнения).
3,4, ..) То же самое добавление в очередь выполнения для следующих итераций, но doStuff () из предыдущего все еще выполняется ...
Как результат - браузер застрял.

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

Serg Hospodarets
источник
1
Я не знаю, почему никто не нашел никакого значения в этом ответе!
Рандика Вишман
9

Я использую setTimeout.

Видимо, разница состоит в том, что setTimeout вызывает метод один раз, setInterval вызывает его повторно.

Вот хорошая статья, объясняющая разницу: Учебник: таймеры JavaScript с setTimeout и setInterval

Bravax
источник
2
Да, я получил эту разницу, но два предоставленных мною кода должны сделать то же самое ...
Дамовиза
Хммм, да ... я бы подумал ... но, согласно dcaunt и его подсчету голосов, это не совсем то, что происходит.
Bravax
9

Ваш код будет иметь разные интервалы выполнения, а в некоторых проектах, например в онлайн-играх, это неприемлемо. Во-первых, что вы должны сделать, чтобы ваш код работал с одинаковыми интервалами, вы должны изменить «myTimeoutFunction» на это:

function myTimeoutFunction()
{
    setTimeout(myTimeoutFunction, 1000);
    doStuff();
}
myTimeoutFunction()

После этого изменения оно будет равно

function myTimeoutFunction()
{
    doStuff();
}
myTimeoutFunction();
setInterval(myTimeoutFunction, 1000);

Но у вас все равно будет нестабильный результат, потому что JS однопоточный. На данный момент, если поток JS чем-то будет занят, он не сможет выполнить вашу функцию обратного вызова, и выполнение будет отложено на 2-3 мсек. Если у вас 60 выполнений в секунду, и каждый раз при случайной задержке 1-3 секунды это будет абсолютно неприемлемо (через одну минуту это будет около 7200 мсек), и я могу посоветовать использовать что-то вроде этого:

    function Timer(clb, timeout) {
        this.clb = clb;
        this.timeout = timeout;
        this.stopTimeout = null;
        this.precision = -1;
    }

    Timer.prototype.start = function() {
        var me = this;
        var now = new Date();
        if(me.precision === -1) {
            me.precision = now.getTime();
        }
        me.stopTimeout = setTimeout(function(){
            me.start()
        }, me.precision - now.getTime() + me.timeout);
        me.precision += me.timeout;
        me.clb();
    };

    Timer.prototype.stop = function() {
        clearTimeout(this.stopTimeout);
        this.precision = -1;
    };

    function myTimeoutFunction()
    {
        doStuff();
    }

    var timer = new Timer(myTimeoutFunction, 1000);
    timer.start();

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

град
источник
7

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

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

Я проверял это на Chrome v23. Я надеюсь, что это детерминированная реализация во всех современных браузерах.

window.setInterval(function(start) {
    console.log('fired: ' + (new Date().getTime() - start));
    wait();
  }, 1000, new Date().getTime());

Консольный вывод:

fired: 1000    + ~2500 ajax call -.
fired: 3522    <------------------'
fired: 6032
fired: 8540
fired: 11048

Эта waitфункция - всего лишь вспомогательный блокирующий поток - синхронный вызов ajax, который занимает ровно 2500 миллисекунд обработки на стороне сервера:

function wait() {
    $.ajax({
        url: "...",
        async: false
    });
}
jwaliszko
источник
1
«Нет ситуации с параллельными итерациями» - да, это должно быть невозможно. Клиентский JavaScript имеет однопоточную модель исполнения, поэтому ничего (обработчики событий и т. Д.) Никогда не происходит одновременно. Вот почему во время синхронного вызова AJAX ничего не происходит (и браузер не отвечает).
MrWhite
Отвечает ли браузер за несколько мс между «интервалами»? Сработает ли другое событие, если оно ожидает? (Спасибо за тесты между прочим +1)
MrWhite
1
Согласно MDN считается « опасным использованием », если функция может выполняться дольше интервала времени. Рекурсивный setTimoutвызов предпочтительнее.
MrWhite
6

Чтобы взглянуть на это немного по-другому: setInterval гарантирует, что код запускается через каждый заданный интервал (т. Е. 1000 мс или сколько вы указываете), в то время как setTimeout устанавливает время, в течение которого он «ждет» запуска кода. А поскольку выполнение кода занимает дополнительные миллисекунды, это добавляет до 1000 мс, и, следовательно, setTimeout запускается снова в неточное время (более 1000 мс).

Например, таймеры / обратные отсчеты не выполняются с помощью setTimeout, они выполняются с помощью setInterval, чтобы гарантировать, что он не задерживается и код выполняется с точно заданным интервалом.

CatalinBerta
источник
5

И setInterval, и setTimeout возвращают идентификатор таймера, который можно использовать для отмены выполнения, то есть до запуска тайм-аутов. Для отмены звоните либо clearInterval, либо clearTimeout, например:

var timeoutId = setTimeout(someFunction, 1000);
clearTimeout(timeoutId);
var intervalId = setInterval(someFunction, 1000),
clearInterval(intervalId);

Кроме того, тайм-ауты автоматически отменяются, когда вы покидаете страницу или закрываете окно браузера.

Хельги
источник
4

Вы можете проверить ответ bobince самостоятельно, запустив следующий javascript или отметив этот JSFiddle.

<div id="timeout"></div>
<div id="interval"></div>

var timeout = 0;
var interval = 0;

function doTimeout(){
    $('#timeout').html(timeout);
    timeout++;
    setTimeout(doTimeout, 1);
}

function doInterval(){
    $('#interval').html(interval);
    interval++;
}

$(function(){
    doTimeout();
    doInterval();
    setInterval(doInterval, 1);
});
Gudradain
источник
3

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

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

Разница очевидна в консоли:

введите описание изображения здесь

testCoder
источник
2

Просто добавление к тому, что уже было сказано, но версия кода setTimeout также достигнет того, Maximum call stack sizeчто остановит его функционирование. Поскольку для рекурсивной функции нет базового варианта, на котором она остановится, вы не сможете запускать ее вечно.

Маац
источник
Я не думаю, что это правда. Смотрите здесь stackoverflow.com/questions/8058612/…
Кевин Уилер
-1

Наиболее общая разница между setInterval (выражение, длительность) заключается в том, что

setTimeout () будет выполнять выражение только ОДИН РАЗ и после этого указанного периода времени.

Однако,

setInterval () будет выполнять выражение в INFINITE-LOOP после каждой указанной продолжительности.

Аншу Адитья
источник
-5

Я думаю SetIntervalи SetTimeoutразные. SetIntervalвыполняет блок в соответствии с установленным временем,SetTimeout блок кода выполняется один раз.

Попробуйте этот набор кодов по истечении времени отсчета секунд:

setInterval(function(e){
    alert('Ugbana Kelvin');
}, 2000);

а потом попробуй

setTimeout(function(e){
    alert('Ugbana Kelvin');
}, 2000);

Вы можете увидеть различия для себя.

Угбана Чуквудзинду Кельвин
источник
Это не отвечает на вопрос. settimeout () может использоваться рекурсивно, чтобы дать тот же результат, что и setinterval. Вопрос был о производительности этих двух вариантов.
Томас Гонсалес