Выполнить функцию setInterval без задержки в первый раз

341

Есть способ настроить setIntervalметод javascript для немедленного выполнения метода и последующего выполнения с таймером.

Jorge
источник
4
Не изначально хотя. Вы можете попробовать позвонить functionодин раз, а затем сделатьsetInterval()
Mrchief

Ответы:

519

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

foo();
setInterval(foo, delay);

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

Таким образом, альтернативный метод должен иметь fooсам триггер для последующих вызовов, используя setTimeoutвместо этого:

function foo() {
   // do stuff
   // ...

   // and schedule a repeat
   setTimeout(foo, delay);
}

// start the cycle
foo();

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

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

(function foo() {
    ...
    setTimeout(foo, delay);
})();

который определяет функцию и запускает цикл сразу.

Альнитак
источник
5
Почему ты предпочитаешь setTimeout ?
Санхьюн Ли
19
@Sangdol, потому что он гарантирует, что события таймера не «складываются», если они остаются необработанными. В некоторых случаях целая масса setIntervalсобытий может прибыть сразу после друг друга без каких-либо задержек.
Альнитак
8
Должен быть какой-то тест, который не позволяет вам размещать в стеке, когда вы устали
Джеймс
3
Как мне остановить функцию, если я использую setTimeoutметод?
Гаурав Бхор
6
@DaveMunger нет, потому что он не является действительно рекурсивным - он только «псевдорекурсивный». «Рекурсивные» вызовы не происходят до тех пор, пока браузер не вернется в цикл обработки событий, после чего стек вызовов будет полностью размотан.
Альнитак
210

Я не уверен, правильно ли я вас понимаю, но вы легко могли бы сделать что-то вроде этого:

setInterval(function hello() {
  console.log('world');
  return hello;
}(), 5000);

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

chjj
источник
16
Это крутой ответ, потому что это именованная функция, которая выполняется немедленно и также возвращает себя. Именно то, что я искал.
jmort253
41
Определенно, это крутое решение, но оно может вызвать путаницу у людей, читающих в будущем.
Дипак Джой
4
Вам не нужно давать ему имя «привет». Вместо этого вы можете вернуть arguments.callee и сделать то же самое с анонимной функцией
JochenJung,
10
@JochenJung arguments.calleeнедоступен в строгом режиме ES5
Alnitak
3
Это тот тип кода Javascript, который смущает большинство новых программистов JS, пришедших с других языков. Напишите несколько подобных вещей, если в вашей компании не так много сотрудников JS и вам нужна работа.
Ян
13

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

Вот мое решение этой проблемы:

function setIntervalImmediately(func, interval) {
  func();
  return setInterval(func, interval);
}

Преимущество этого решения:

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

Пример:

// create 1 second interval with immediate execution
var myInterval = setIntervalImmediately( _ => {
        console.log('hello');
    }, 1000);

// clear interval after 4.5 seconds
setTimeout( _ => {
        clearInterval(myInterval);
    }, 4500);

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

var setIntervalOrig = setInterval;

setInterval = function(func, interval) {
    func();
    return setIntervalOrig(func, interval);
}

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

Йенс Вирт
источник
Я предпочитаю это решение, а не setTimeoutрешение, потому что оно возвращает setIntervalобъект. Чтобы избежать вызова функций, которые могут быть позже в коде и, следовательно, не определены в текущее время, я обертываю первый вызов функции в setTimeoutфункцию следующим образом: setTimeout(function(){ func();},0);первая функция затем вызывается после текущего цикла обработки, который также сразу , но больше ошибок доказано.
Pensan
8

Вы можете setInterval()добавить функцию, которая обеспечивает такое поведение:

function instantGratification( fn, delay ) {
    fn();
    setInterval( fn, delay );
}

... тогда используйте это так:

instantGratification( function() {
    console.log( 'invoked' );
}, 3000);
user113716
источник
5
Я думаю, вы должны вернуть то, что возвращает setInterval. Это потому, что в противном случае вы не можете использовать clearInterval.
Хенрик Р
5

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

(function() {
    var originalSetInterval = window.setInterval;

    window.setInterval = function(fn, delay, runImmediately) {
        if(runImmediately) fn();
        return originalSetInterval(fn, delay);
    };
})();

Установите для третьего аргумента setInterval значение true, и он будет запущен в первый раз сразу после вызова setInterval:

setInterval(function() { console.log("hello world"); }, 5000, true);

Или пропустите третий аргумент, и он сохранит свое первоначальное поведение:

setInterval(function() { console.log("hello world"); }, 5000);

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

Mahn
источник
9
Переопределение нативных функций браузера ужасно, так как оно может нарушить другой сосуществующий код при изменении спецификации. Фактически, setInterval в настоящее время имеет больше параметров: developer.mozilla.org/en-US/docs/Web/API/WindowTimers/…
Аксель Костас Пена
2

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

(function f() {
    this.emit("...");
    setTimeout(f.bind(this), 1000);
}).bind(this)();

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

(that => {
    (function f() {
        that.emit("...");
        setTimeout(f, 1000);
    })();
})(this);

Или , может быть , рассмотреть возможность использования в @autobindдекоратора в зависимости от вашего кода.

Константин Ван
источник
1

Я предложу вызывать функции в следующей последовательности

var _timer = setInterval(foo, delay, params);
foo(params)

Вы также можете передать в _timerFoo, если вы хотите clearInterval(_timer)при определенных условиях

var _timer = setInterval(function() { foo(_timer, params) }, delay);
foo(_timer, params);
Джаянт Варшней
источник
Не могли бы вы объяснить это более подробно?
Полидукс
Вы устанавливаете таймер от второго вызова, в первый раз вы просто вызываете FN напрямую.
Джаянт Варшней
1

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

//Declare your function here
function My_Function(){
  console.log("foo");
}    

//Call the function first
My_Function();

//Set the interval
var interval = window.setInterval( My_Function, 500 );

Polyducks
источник
1
Это именно то, что принятый ответ делает в первых нескольких строках. Как этот ответ добавляет что-то новое?
Дан Даскалеску
В принятом ответе говорится, что не делать этого. Мой ответ на принятый ответ объясняет, почему это все еще допустимый метод. ОП специально запрашивает вызов функции setInterval при первом вызове, но принятый ответ отвлекает, чтобы рассказать о преимуществах setTimeout.
Полидукс
0

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

function foo(){ ... }

window.onload = function() {
   foo();
};

window.setInterval(function()
{
    foo(); 
}, 5000);
Dario
источник
0

Есть удобный пакет npm под названием firstInterval (полное раскрытие, он мой).

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

Этот шаблон

setInterval(callback, 1000, p1, p2);
callback(p1, p2);

идентично

firstInterval(callback, 1000, p1, p2);

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

jimm101
источник
0

// YCombinator
function anonymous(fnc) {
  return function() {
    fnc.apply(fnc, arguments);
    return fnc;
  }
}

// Invoking the first time:
setInterval(anonymous(function() {
  console.log("bar");
})(), 4000);

// Not invoking the first time:
setInterval(anonymous(function() {
  console.log("foo");
}), 4000);
// Or simple:
setInterval(function() {
  console.log("baz");
}, 4000);

Хорошо, это так сложно, поэтому позвольте мне сказать проще:

function hello(status ) {    
  console.log('world', ++status.count);
  
  return status;
}

setInterval(hello, 5 * 1000, hello({ count: 0 }));

НРД
источник
Это значительно более спроектировано.
Полидукс
0

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

var delay = 100;

function foo() {
  console.log("Change initial delay-time to what you want.");
  delay = 12000;
  setTimeout(foo, delay);
}

reo_yamanaka
источник
-2

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

Пример кода с РЕАЛЬНОЙ нулевой задержкой, который работает в Chrome, Safari, Opera

function setZeroTimeout(callback) {
var channel = new MessageChannel();
channel.port1.onmessage = callback;
channel.port2.postMessage('');
}

Вы можете найти больше информации здесь

И после первого ручного вызова вы можете создать интервал с вашей функцией.

Xe-Xe
источник
-10

на самом деле самое быстрое, чтобы сделать

interval = setInterval(myFunction(),45000)

это вызовет myfunction, а затем будет делать это каждые 45 секунд, что отличается от выполнения

interval = setInterval(myfunction, 45000)

который не будет называть это, но запланировать это только

Герцель Арменгол
источник
С чего взял?
Кен Шарп
2
Это работает, только если myFunction()возвращает себя. Вместо того, чтобы модифицировать каждую функцию, вызываемую setIntervalею, лучше подходить setIntervalодин раз, как предлагают другие ответы.
Йенс Вирт