Exécutez la fonction setInterval sans délai la première fois

341

C'est là un moyen de configurer la setIntervalméthode de javascript pour exécuter la méthode immédiatement, puis s'exécute avec le minuteur

Jorge
la source
4
Pas nativement cependant. Vous pouvez essayer d'appeler functionune fois puis de faire lesetInterval()
Mrchief

Réponses:

519

Il est plus simple d'appeler directement la fonction directement la première fois:

foo();
setInterval(foo, delay);

Cependant, il y a de bonnes raisons d'éviter setInterval- en particulier dans certaines circonstances, toute une série d' setIntervalévénements peuvent arriver immédiatement les uns après les autres sans aucun retard. Une autre raison est que si vous voulez arrêter la boucle, vous devez appeler explicitement, clearIntervalce qui signifie que vous devez vous souvenir du handle renvoyé par l' setIntervalappel d' origine .

Donc, une autre méthode consiste à se foodéclencher pour les appels suivants en utilisant à la setTimeoutplace:

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

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

// start the cycle
foo();

Cela garantit qu'il y a au moins un intervalle delayentre les appels. Cela facilite également l'annulation de la boucle si nécessaire - vous n'appelez simplement pas setTimeoutlorsque votre condition de fin de boucle est atteinte.

Mieux encore, vous pouvez envelopper tout cela dans une expression de fonction immédiatement invoquée qui crée la fonction, qui se rappelle ensuite comme ci-dessus et démarre automatiquement la boucle:

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

qui définit la fonction et démarre le cycle d'un seul coup.

Alnitak
la source
5
Pourquoi préférez-vous setTimeout?
Sanghyun Lee
19
@Sangdol, car il garantit que les événements de la minuterie ne se "cumulent" pas s'ils ne sont pas traités. Dans certaines circonstances, toute une série d' setIntervalévénements peut arriver immédiatement les uns après les autres sans aucun retard.
Alnitak
8
Il devrait y avoir une sorte de test qui ne vous permet pas de publier sur la pile lorsque vous êtes fatigué
James
3
Comment arrêter la fonction si j'utilise la setTimeoutméthode?
Gaurav Bhor
6
@DaveMunger non, car ce n'est pas vraiment récursif - c'est seulement "pseudo-récursif". Les appels "récursifs" ne se produisent pas jusqu'à ce que le navigateur revienne à la boucle d'événement, moment auquel la pile d'appels a été complètement déroulée.
Alnitak
210

Je ne sais pas si je vous comprends bien, mais vous pourriez facilement faire quelque chose comme ça:

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

Il y a évidemment un certain nombre de façons de procéder, mais c'est la façon la plus concise à laquelle je peux penser.

chjj
la source
16
C'est une bonne réponse car c'est une fonction nommée qui s'exécute immédiatement et se retourne également. Exactement ce que je cherchais.
jmort253
41
Certainement une solution intéressante, mais susceptible de semer la confusion chez les personnes qui liront à l'avenir.
Deepak Joy
4
Vous n'avez pas besoin de lui donner un nom «bonjour». Vous pouvez à la place retourner arguments.callee et avoir la même chose avec une fonction anonyme
JochenJung
10
@JochenJung arguments.calleen'est pas disponible en mode strict ES5
Alnitak
3
C'est le genre de code Javascript qui confondra la plupart des nouveaux programmeurs JS venus d'autres langues. Écrivez un tas de trucs comme ça si votre entreprise n'a pas beaucoup de personnel JS et que vous voulez la sécurité de l'emploi.
Ian
13

Je suis tombé sur cette question en raison du même problème, mais aucune des réponses n'aide si vous devez vous comporter exactement comme setInterval()mais avec la seule différence que la fonction est appelée immédiatement au début.

Voici ma solution à ce problème:

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

L'avantage de cette solution:

  • le code existant en utilisant setIntervalpeut facilement être adapté par substitution
  • fonctionne en mode strict
  • il fonctionne avec les fonctions et fermetures nommées existantes
  • vous pouvez toujours utiliser la valeur de retour et la transmettre à clearInterval()plus tard

Exemple:

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

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

Pour être effronté, si vous avez vraiment besoin d'utiliser, setIntervalvous pouvez également remplacer l'original setInterval. Par conséquent, aucun changement de code n'est requis lors de l'ajout avant votre code existant:

var setIntervalOrig = setInterval;

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

Pourtant, tous les avantages énumérés ci-dessus s'appliquent ici, mais aucune substitution n'est nécessaire.

Jens Wirth
la source
Je préfère cette solution à la setTimeoutsolution car elle renvoie un setIntervalobjet. Pour éviter d'appeler des fonctions qui sont peut-être plus tard dans le code et donc indéfinies à l'heure actuelle, j'encapsule le premier appel de fonction dans une setTimeoutfonction comme celle-ci: setTimeout(function(){ func();},0);La première fonction est ensuite appelée après le cycle de traitement en cours, qui est également immédiatement , mais est plus d'erreur prouvée.
pensan
8

Vous pouvez encapsuler setInterval()une fonction qui fournit ce comportement:

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

... puis utilisez-le comme ceci:

instantGratification( function() {
    console.log( 'invoked' );
}, 3000);
user113716
la source
5
Je pense que vous devriez retourner ce que setInterval renvoie. C'est parce que sinon vous ne pouvez pas utiliser clearInterval.
Henrik R
5

Voici un wrapper pour le faire si vous en avez besoin:

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

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

Définissez le troisième argument de setInterval sur true et il s'exécutera pour la première fois immédiatement après l'appel de setInterval:

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

Ou omettez le troisième argument et il conservera son comportement d'origine:

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

Certains navigateurs prennent en charge des arguments supplémentaires pour setInterval que ce wrapper ne prend pas en compte; Je pense que ceux-ci sont rarement utilisés, mais gardez cela à l'esprit si vous en avez besoin.

Mahn
la source
9
Remplacer les fonctions natives du navigateur est terrible car il peut briser d'autres codes coexistants lorsque les spécifications changent. En fait, setInterval a actuellement plus de paramètres: developer.mozilla.org/en-US/docs/Web/API/WindowTimers/…
Áxel Costas Pena
2

Car quelqu'un a besoin d'amener l'extérieur thiscomme s'il s'agissait d'une fonction de flèche.

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

Si les déchets ci-dessus vous dérangent, vous pouvez faire une fermeture à la place.

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

Ou peut-être envisagez d'utiliser le @autobinddécorateur en fonction de votre code.

Константин Ван
la source
1

Je proposerai d'appeler les fonctions dans l'ordre suivant

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

Vous pouvez également passer _timerau foo, si vous le souhaitez à clearInterval(_timer)une certaine condition

var _timer = setInterval(function() { foo(_timer, params) }, delay);
foo(_timer, params);
Jayant Varshney
la source
Pourriez-vous expliquer cela plus en profondeur?
Polyducks
vous réglez la minuterie à partir du deuxième appel, pour la première fois vous appelez directement le fn directement.
Jayant Varshney
1

Voici une version simple pour les novices sans tous les ennuis. Il déclare simplement la fonction, l'appelle, puis démarre l'intervalle. C'est tout.

//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
la source
1
C'est exactement ce que fait la réponse acceptée dans la première ligne. Comment cette réponse ajoute-t-elle quelque chose de nouveau?
Dan Dascalescu
La réponse acceptée poursuit en disant de ne pas faire cela. Ma réponse à la réponse acceptée explique pourquoi c'est toujours une méthode valide. L'OP demande spécifiquement d'appeler la fonction setInterval lors de son premier appel, mais la réponse acceptée se détourne pour parler des avantages de setTimeout.
Polyducks
0

Pour résoudre ce problème, j'exécute la fonction une première fois après le chargement de la page.

function foo(){ ... }

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

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

Il existe un package npm pratique appelé firstInterval (divulgation complète, c'est le mien).

La plupart des exemples ici n'incluent pas la gestion des paramètres, et changer les comportements par défaut de setIntervaltout grand projet est mauvais. De la documentation:

Ce modèle

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

est identique à

firstInterval(callback, 1000, p1, p2);

Si vous êtes de la vieille école dans le navigateur et que vous ne voulez pas de dépendance, c'est un simple copier-coller du code.

jimm101
la source
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, c'est tellement complexe, alors, permettez-moi de le dire plus simplement:

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

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

NSD
la source
C'est largement trop conçu.
Polyducks
0

Vous pouvez définir un temps de retard initial très petit (par exemple 100) et le régler sur le temps de retard souhaité dans la fonction:

var delay = 100;

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

reo_yamanaka
la source
-2

Il y a un problème avec l'appel asynchrone immédiat de votre fonction, car setTimeout / setInterval standard a un délai minimal d'environ plusieurs millisecondes même si vous le définissez directement sur 0. Cela est dû à un travail spécifique au navigateur.

Un exemple de code avec un REAL zéro retard qui fonctionne dans Chrome, Safari, Opera

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

Vous pouvez trouver plus d'informations ici

Et après le premier appel manuel, vous pouvez créer un intervalle avec votre fonction.

Xe-Xe
la source
-10

en fait le plus rapide est de faire

interval = setInterval(myFunction(),45000)

cela appellera ma fonction, puis le fera toutes les 45 secondes, ce qui est différent de faire

interval = setInterval(myfunction, 45000)

qui ne l'appellera pas, mais le planifiera seulement

Hertzel Armengol
la source
Où est-ce que tu as trouvé ça?
Ken Sharp
2
Cela ne fonctionne que si se myFunction()retourne. Au lieu de cela, modifier chaque fonction à appeler setIntervalest une meilleure approche pour boucler setIntervalune fois comme les autres réponses le proposent.
Jens Wirth