Execute a função setInterval sem demora na primeira vez

339

Existe uma maneira de configurar o setIntervalmétodo de javascript para executar o método imediatamente e depois executar com o timer

Jorge
fonte
4
Não nativamente embora. Você pode tentar ligar functionuma vez e depois fazer osetInterval()
Mrchief

Respostas:

519

É mais simples chamar a função diretamente diretamente na primeira vez:

foo();
setInterval(foo, delay);

No entanto, existem boas razões para evitar setInterval- em particular em algumas circunstâncias, uma grande quantidade de setIntervaleventos pode chegar imediatamente um após o outro, sem demora. Outro motivo é que, se você deseja interromper o loop, deve ligar explicitamente, o clearIntervalque significa que é necessário lembrar o identificador retornado da setIntervalchamada original .

Portanto, um método alternativo é fooacionar-se para chamadas subseqüentes usando setTimeout:

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

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

// start the cycle
foo();

Isso garante que haja pelo menos um intervalo delayentre as chamadas. Isso também facilita o cancelamento do loop, se necessário - você não liga setTimeoutquando a condição de encerramento do loop é atingida.

Melhor ainda, você pode agrupar tudo isso em uma expressão de função chamada imediatamente que cria a função, que então se chama novamente como acima, e inicia automaticamente o loop:

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

que define a função e inicia o ciclo de uma só vez.

Alnitak
fonte
5
Por que você prefere setTimeout?
Sanghyun Lee
19
@Sangdol porque garante que os eventos do timer não "empilhem" se forem deixados sem processamento. Em algumas circunstâncias, toda uma carga de setIntervaleventos pode chegar imediatamente um após o outro, sem demora.
Alnitak 29/05
8
Deve haver algum tipo de teste que não permite que você postar na pilha quando você está cansado
James
3
Como interrompo a função se eu uso o setTimeoutmétodo?
Gaurav Bhor
6
@DaveMunger não, porque não é verdadeiramente recursivo - é apenas "pseudo-recursivo". As chamadas "recursivas" não acontecem até que o navegador retorne ao loop de eventos, momento em que a pilha de chamadas foi completamente desenrolada.
Alnitak
209

Não tenho certeza se estou entendendo corretamente, mas você poderia facilmente fazer algo assim:

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

Obviamente, existem várias maneiras de fazer isso, mas é a maneira mais concisa em que consigo pensar.

chjj
fonte
16
Essa é uma resposta interessante porque é uma função nomeada que é executada imediatamente e também retorna automaticamente. Exatamente o que eu estava procurando.
jmort253
41
Definitivamente, uma solução interessante, mas provavelmente causará confusão para as pessoas que lerem no futuro.
Deepak Joy
4
Você não precisa dar um nome 'olá'. Você poderia, em vez retornar arguments.callee e têm o mesmo com uma função anônima
JochenJung
10
O @JochenJung arguments.calleenão está disponível no modo estrito ES5
Alnitak
3
Esse é o tipo de código Javascript que confundirá a maioria dos novos programadores JS que vieram de outros idiomas. Escreva um monte de coisas assim se sua empresa não tiver muito pessoal da JS e desejar segurança no emprego.
21717 Ian
13

Eu me deparei com essa pergunta devido ao mesmo problema, mas nenhuma das respostas ajuda se você precisar se comportar exatamente como o normal,setInterval() mas com a única diferença de que a função é chamada imediatamente no início.

Aqui está a minha solução para este problema:

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

A vantagem desta solução:

  • o código existente setIntervalpode ser facilmente adaptado por substituição
  • funciona em modo estrito
  • funciona com funções e fechamentos existentes existentes
  • você ainda pode usar o valor de retorno e passá-lo para clearInterval()mais tarde

Exemplo:

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

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

Para ser atrevido, se você realmente precisar usar setInterval, também poderá substituir o original setInterval. Portanto, nenhuma alteração de código é necessária ao adicionar isso antes do código existente:

var setIntervalOrig = setInterval;

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

Ainda assim, todas as vantagens listadas acima se aplicam aqui, mas nenhuma substituição é necessária.

Jens Wirth
fonte
Eu prefiro esta solução sobre a setTimeoutsolução porque ela retorna um setIntervalobjeto. Para evitar chamar funções que talvez estejam mais tarde no código e, portanto, indefinidas no momento atual, envolvo a primeira chamada de função em uma setTimeoutfunção como esta: setTimeout(function(){ func();},0);A primeira função é chamada após o ciclo de processamento atual, que também é imediato , mas é mais erros comprovados.
pensan
8

Você pode agrupar setInterval()uma função que fornece esse comportamento:

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

... então use-o assim:

instantGratification( function() {
    console.log( 'invoked' );
}, 3000);
user113716
fonte
5
Eu acho que você deve retornar o que setInterval retorna. Isso ocorre porque, caso contrário, você não pode usar clearInterval.
Henrik R
5

Aqui está um invólucro para analisá-lo, se você precisar:

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

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

Defina o terceiro argumento de setInterval como true e ele será executado pela primeira vez imediatamente após chamar setInterval:

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

Ou omita o terceiro argumento e ele manterá seu comportamento original:

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

Alguns navegadores suportam argumentos adicionais para setInterval que esse wrapper não leva em consideração; Penso que estes raramente são utilizados, mas tenha isso em mente se você precisar deles.

Mahn
fonte
9
A substituição de funções nativas do navegador é terrível, pois pode quebrar outro código coexistente quando as especificações mudam. Na verdade, setInterval tem atualmente mais parâmetros: developer.mozilla.org/en-US/docs/Web/API/WindowTimers/...
Axel Costas Pena
2

Para alguém precisa trazer o exterior para thisdentro como se fosse uma função de seta.

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

Se o lixo produzido acima o incomoda, você pode fazer um fechamento.

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

Ou talvez considere usar o @autobinddecorador, dependendo do seu código.

Константин Ван
fonte
1

Vou sugerir a chamada das funções na seguinte sequência

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

Você também pode passar _timerpara o foo, se quiser clearInterval(_timer)em uma determinada condição

var _timer = setInterval(function() { foo(_timer, params) }, delay);
foo(_timer, params);
Jayant Varshney
fonte
Você poderia explicar isso com mais profundidade?
31418 Polyducks #
você ajusta o cronômetro a partir da segunda chamada; pela primeira vez, basta ligar diretamente para o fn.
Jayant Varshney
1

Aqui está uma versão simples para iniciantes, sem toda a confusão. Apenas declara a função, chama e depois inicia o intervalo. É isso aí.

//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
fonte
11
É exatamente isso que a resposta aceita faz na primeira linha. Como essa resposta adiciona algo novo?
Dan Dascalescu 23/09
A resposta aceita continua dizendo para não fazer isso. Minha resposta à resposta aceita explica por que ainda é um método válido. O OP solicita especificamente chamar a função de setInterval em sua primeira chamada, mas a resposta aceita desvia para falar sobre os benefícios de setTimeout.
Polyducks
0

Para resolver esse problema, eu executo a função pela primeira vez após o carregamento da página.

function foo(){ ... }

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

window.setInterval(function()
{
    foo(); 
}, 5000);
dario
fonte
0

Existe um pacote npm conveniente chamado firstInterval (divulgação completa, é meu).

Muitos dos exemplos aqui não incluem manipulação de parâmetros, e alterar comportamentos padrão de setIntervalqualquer projeto grande é ruim. Dos documentos:

Esse padrão

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

é idêntico a

firstInterval(callback, 1000, p1, p2);

Se você é da velha escola no navegador e não quer a dependência, é fácil copiar e colar do código.

jimm101
fonte
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);

Ok, isso é tão complexo, então, deixe-me simplificar:

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

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

NSD
fonte
Isso é muito mais que engenharia.
Polyducks
0

Você pode definir um atraso inicial muito pequeno (por exemplo, 100) e configurá-lo para o atraso desejado na função:

var delay = 100;

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

reo_yamanaka
fonte
-2

Há um problema com a chamada assíncrona imediata de sua função, porque o setTimeout / setInterval padrão tem um tempo limite mínimo de vários milissegundos, mesmo que você o defina diretamente como 0. Isso foi causado por um trabalho específico do navegador.

Um exemplo de código com um atraso zero REAL que funciona no Chrome, Safari, Opera

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

Você pode encontrar mais informações aqui

E após a primeira chamada manual, você pode criar um intervalo com sua função.

Xe-Xe
fonte
-10

na verdade, o mais rápido é fazer

interval = setInterval(myFunction(),45000)

isso chamará de função e, em seguida, fará isso de novo a cada 45 segundos, o que é diferente de fazer

interval = setInterval(myfunction, 45000)

que não vai chamar, mas agendar apenas

Hertzel Armengol
fonte
Onde você conseguiu aquilo?
Ken Sharp
2
Isso funciona apenas se myFunction()retornar automaticamente. Em vez disso, modificar cada função a ser chamada por setIntervalela é uma abordagem melhor para agrupar setIntervaluma vez, como as outras respostas estão propondo.
Jens Wirth