web-dev-qa-db-fra.com

Comment puis-je passer un paramètre à un rappel setTimeout ()?

J'ai du code JavaScript qui ressemble à:

function statechangedPostQuestion()
{
  //alert("statechangedPostQuestion");
  if (xmlhttp.readyState==4)
  {
    var topicId = xmlhttp.responseText;
    setTimeout("postinsql(topicId)",4000);
  }
}

function postinsql(topicId)
{
  //alert(topicId);
}

Je reçois un message d'erreur indiquant que topicId n'est pas défini Tout fonctionnait avant d'utiliser la fonction setTimeout()

Je souhaite que ma fonction postinsql(topicId) soit appelée après un certain temps. Que devrais-je faire?

727
Zeeshan Rang
setTimeout(function() {
    postinsql(topicId);
}, 4000)

Vous devez définir une fonction anonyme en tant que paramètre au lieu d'une chaîne. Cette dernière méthode ne devrait même pas fonctionner conformément à la spécification ECMAScript, mais les navigateurs sont tout simplement indulgents. Ceci est la solution appropriée. Ne vous fiez jamais à passer une chaîne en tant que 'fonction' lorsque vous utilisez setTimeout() ou setInterval(), c'est plus lent car il doit être évalué et ce n'est tout simplement pas correct.

METTRE À JOUR:

Comme Hobblin l'a dit dans ses commentaires sur la question, vous pouvez maintenant passer des arguments à la fonction dans setTimeout à l'aide de Function.prototype.bind()

Exemple:

setTimeout(postinsql.bind(null, topicId), 4000);
1010
meder omuraliev

Dans les navigateurs modernes, "setTimeout" reçoit un troisième paramètre qui est envoyé en tant que paramètre à la fonction interne à la fin du temporisateur.

Exemple:

var hello = "Hello World";
setTimeout(alert, 1000, hello);

Plus de détails:

626
Fabio Phms

Après quelques recherches et tests, la seule implémentation correcte est:

setTimeout(yourFunctionReference, 4000, param1, param2, paramN);

setTimeout transmettra tous les paramètres supplémentaires à votre fonction afin qu'ils puissent y être traités.

La fonction anonyme peut fonctionner pour des choses très basiques, mais dans une instance d'objet où vous devez utiliser "this", il n'y a aucun moyen de le faire fonctionner . Toute fonction anonyme changera "this" pour qu'il pointe vers la fenêtre de sorte que vous perdrez votre référence d'objet.

121
Jiri Vetyska

C'est une très vieille question avec une réponse déjà "correcte" mais je pensais mentionner une autre approche que personne n'a mentionnée ici. Ceci est copié-collé depuis l'excellent underscore library:

_.delay = function(func, wait) {
  var args = slice.call(arguments, 2);
  return setTimeout(function(){ return func.apply(null, args); }, wait);
};

Vous pouvez transmettre autant d'arguments que vous le souhaitez à la fonction appelée par setTimeout et en tant que bonus supplémentaire (enfin, généralement un bonus), la valeur des arguments transmis à votre fonction est gelée lorsque vous appelez setTimeout. s'ils changent de valeur à un moment donné entre le moment où setTimeout () est appelée et le moment où il arrive à expiration, eh bien… ce n'est plus une frustration aussi hideuse :)

Voici un violon où vous pouvez voir ce que je veux dire.

43
David Meister

Je suis récemment tombé sur la situation unique de devoir utiliser une setTimeout dans une boucle. Comprendre cela peut vous aider à comprendre comment passer des paramètres à setTimeout.

Méthode 1

Utilisez forEach et Object.keys, conformément à la suggestion de Sukima:

var testObject = {
    prop1: 'test1',
    prop2: 'test2',
    prop3: 'test3'
};

Object.keys(testObject).forEach(function(propertyName, i) {
    setTimeout(function() {
        console.log(testObject[propertyName]);
    }, i * 1000);
});

Je recommande cette méthode.

Méthode 2

Utilisez bind:

var i = 0;
for (var propertyName in testObject) {
    setTimeout(function(propertyName) {
        console.log(testObject[propertyName]);
    }.bind(this, propertyName), i++ * 1000);
}

JSFiddle: http://jsfiddle.net/MsBkW/

Méthode 3

Ou si vous ne pouvez pas utiliser forEach ou bind, utilisez un IIFE :

var i = 0;
for (var propertyName in testObject) {
    setTimeout((function(propertyName) {
        return function() {
            console.log(testObject[propertyName]);
        };
    })(propertyName), i++ * 1000);
}

Méthode 4

Mais si vous ne vous souciez pas de IE <10, vous pouvez utiliser la suggestion de Fabio suggestion :

var i = 0;
for (var propertyName in testObject) {
    setTimeout(function(propertyName) {
        console.log(testObject[propertyName]);
    }, i++ * 1000, propertyName);
}

Méthode 5 (ES6)

Utilisez une variable de portée de bloc:

let i = 0;
for (let propertyName in testObject) {
    setTimeout(() => console.log(testObject[propertyName]), i++ * 1000);
}

Bien que je recommande quand même d’utiliser Object.keys avec forEach dans ES6.

36
David Sherret

Hobblin a déjà commenté la question, mais ça devrait être une réponse!

Utiliser Function.prototype.bind() est le moyen le plus propre et le plus flexible de le faire (avec l'avantage supplémentaire de pouvoir définir le contexte this):

setTimeout(postinsql.bind(null, topicId), 4000);

Pour plus d'informations, voir ces liens MDN:
https://developer.mozilla.org/en/docs/DOM/window.setTimeout#highlighter_547041https://developer.mozilla.org/en/docs/JavaScript/Reference/Global_Objects/Function/bind # With_setTimeout

22
dain

Certaines réponses sont correctes mais compliquées.

Je réponds encore une fois, 4 ans plus tard, parce que je me heurte encore à un code trop complexe pour résoudre exactement cette question. Là IS une solution élégante.

Tout d'abord, ne transmettez pas une chaîne en tant que premier paramètre lors de l'appel de setTimeout, car elle appelle en réalité un appel à la fonction "eval" lente.

Alors, comment pouvons-nous passer un paramètre à une fonction de délai d'attente? En utilisant la fermeture:

settopic=function(topicid){
  setTimeout(function(){
    //thanks to closure, topicid is visible here
    postinsql(topicid);
  },4000);
}

...
if (xhr.readyState==4){
  settopic(xhr.responseText);
}

Certains ont suggéré d'utiliser une fonction anonyme lors de l'appel de la fonction de délai d'attente:

if (xhr.readyState==4){
  setTimeout(function(){
    settopic(xhr.responseText);
  },4000);
}

La syntaxe fonctionne. Mais au moment où settopic est appelé, c'est-à-dire 4 secondes plus tard, l'objet XHR peut ne pas être le même. Il est donc important de pré-lier les variables .

14
Schien

Remplacer 

 setTimeout("postinsql(topicId)", 4000);

avec

 setTimeout("postinsql(" + topicId + ")", 4000);

ou mieux encore, remplacez l'expression de chaîne par une fonction anonyme

 setTimeout(function () { postinsql(topicId); }, 4000);

MODIFIER:

Le commentaire de Brownstone est incorrect, cela fonctionnera comme prévu, comme le démontre son exécution dans la console Firebug

(function() {
  function postinsql(id) {
    console.log(id);
  }
  var topicId = 3
  window.setTimeout("postinsql(" + topicId + ")",4000); // outputs 3 after 4 seconds
})();

Notez que je suis d’accord avec les autres pour éviter de passer une chaîne à setTimeout car cela appelle eval() sur la chaîne et transmet plutôt une fonction.

9
Russ Cam

Ma réponse:

setTimeout((function(topicId) {
  return function() {
    postinsql(topicId);
  };
})(topicId), 4000);

Explication:

La fonction anonyme créée renvoie une autre fonction anonyme. Cette fonction a accès à la variable topicId initialement passée, elle ne fera donc pas d'erreur. La première fonction anonyme est immédiatement appelée, en passant topicId, de sorte que la fonction enregistrée avec un retard a accès à topicId au moment de l'appel, par le biais de fermetures.

OR

Cela convertit essentiellement à:

setTimeout(function() {
  postinsql(topicId); // topicId inside higher scope (passed to returning function)
}, 4000);

EDIT: J'ai vu la même réponse, alors regardez le sien. Mais je n'ai pas volé sa réponse! J'ai juste oublié de regarder. Lisez l'explication et voyez si cela aide à comprendre le code.

9
user4447514

La solution multi-navigateurs la plus simple pour prendre en charge les paramètres dans setTimeout:

setTimeout(function() {
    postinsql(topicId);
}, 4000)

Si cela ne vous dérange pas de ne pas prendre en charge IE 9 et versions antérieures:

setTimeout(postinsql, 4000, topicId);

setTimeout desktop browser compatibility

setTimeout mobile browser compatibility

https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout

5

Je sais que c'est vieux, mais je voulais ajouter ma saveur (préférée) à cela.

Je pense qu'un moyen assez lisible pour y parvenir est de passer la topicId à une fonction, qui à son tour utilise l'argument pour référencer l'ID de sujet en interne. Cette valeur ne changera pas même si topicId à l'extérieur sera modifié peu de temps après.

var topicId = xmlhttp.responseText;
var fDelayed = function(tid) {
  return function() {
    postinsql(tid);
  };
}
setTimeout(fDelayed(topicId),4000);

ou court:

var topicId = xmlhttp.responseText;
setTimeout(function(tid) {
  return function() { postinsql(tid); };
}(topicId), 4000);
3
Dominic

cela fonctionne dans tous les navigateurs (IE est un bizarre)

setTimeout( (function(x) {
return function() {
        postinsql(x);
    };
})(topicId) , 4000);
3
user3756459

La réponse de David Meister semble prendre en compte les paramètres susceptibles de changer immédiatement après l'appel de setTimeout () mais avant l'appel de la fonction anonyme. Mais c'est trop lourd et pas très évident. J'ai découvert une manière élégante de faire à peu près la même chose avec IIFE (expression de fonction immédiatement invikée).

Dans l'exemple ci-dessous, la variable currentList est transmise à l'IIFE, qui l'enregistre dans sa fermeture jusqu'à l'appel de la fonction retardée. Même si la variable currentList change immédiatement après le code affiché, la fonction setInterval() fera le bon choix.

Sans cette technique IIFE, la fonction setTimeout() sera certainement appelée pour chaque élément h2 du DOM, mais tous ces appels ne verront que la valeur textuelle de l'élément last h2.

<script>
  // Wait for the document to load.
  $(document).ready(function() {
  $("h2").each(function (index) {

    currentList = $(this).text();

    (function (param1, param2) {
        setTimeout(function() {
            $("span").text(param1 + ' : ' + param2 );
        }, param1 * 1000);

    })(index, currentList);
  });
</script>
3
Gurjeet Singh

Comment j'ai résolu cette étape?

juste comme ça :

setTimeout((function(_deepFunction ,_deepData){
    var _deepResultFunction = function _deepResultFunction(){
          _deepFunction(_deepData);
    };
    return _deepResultFunction;
})(fromOuterFunction, fromOuterData ) , 1000  );

setTimeout attend une référence à une fonction, je l'ai donc créée dans une fermeture, qui interprète mes données et renvoie une fonction avec une bonne instance de mes données!

Peut-être que vous pouvez améliorer cette partie:

_deepFunction(_deepData);

// change to something like :
_deepFunction.apply(contextFromParams , args); 

Je l’ai testé sur chrome, firefox et IE et il s’exécute bien. Je ne connais pas les performances mais j’avais besoin que cela fonctionne.

un échantillon de test: 

myDelay_function = function(fn , params , ctxt , _time){
setTimeout((function(_deepFunction ,_deepData, _deepCtxt){
            var _deepResultFunction = function _deepResultFunction(){
                //_deepFunction(_deepData);
                _deepFunction.call(  _deepCtxt , _deepData);
            };
        return _deepResultFunction;
    })(fn , params , ctxt)
, _time) 
};

// the function to be used :
myFunc = function(param){ console.log(param + this.name) }
// note that we call this.name

// a context object :
myObjet = {
    id : "myId" , 
    name : "myName"
}

// setting a parmeter
myParamter = "I am the outer parameter : ";

//and now let's make the call :
myDelay_function(myFunc , myParamter  , myObjet , 1000)

// this will produce this result on the console line :
// I am the outer parameter : myName

Peut-être que vous pouvez changer la signature pour la rendre plus compatible:

myNass_setTimeOut = function (fn , _time , params , ctxt ){
return setTimeout((function(_deepFunction ,_deepData, _deepCtxt){
            var _deepResultFunction = function _deepResultFunction(){
                //_deepFunction(_deepData);
                _deepFunction.apply(  _deepCtxt , _deepData);
            };
        return _deepResultFunction;
    })(fn , params , ctxt)
, _time) 
};

// and try again :
for(var i=0; i<10; i++){
   myNass_setTimeOut(console.log ,1000 , [i] , console)
}

Et enfin pour répondre à la question initiale:

 myNass_setTimeOut( postinsql, 4000, topicId );

J'espère que ça peut aider! 

ps: désolé mais anglais ce n'est pas ma langue maternelle!

2
Anonymous0day

Notez que la raison pour laquelle topicId était "non défini" d'après le message d'erreur est qu'elle existait en tant que variable locale lors de l'exécution de setTimeout, mais pas lorsque l'appel retardé de postinsql s'est produit. Il est particulièrement important de faire attention à la durée de vie variable, en particulier lorsque vous essayez quelque chose comme passer "ceci" comme référence d'objet.

J'ai entendu dire que vous pouvez transmettre topicId en tant que troisième paramètre à la fonction setTimeout. Peu de détails sont fournis, mais j'ai suffisamment d'informations pour le faire fonctionner, et c'est un succès dans Safari. Je ne sais pas ce qu'ils veulent dire par "l'erreur de la milliseconde". Vérifiez le ici:

http://www.howtocreate.co.uk/tutorials/javascript/timers

2
billy

En général, si vous devez passer une fonction en tant que rappel avec des paramètres spécifiques, vous pouvez utiliser des fonctions d'ordre supérieur. C'est assez élégant avec ES6:

const someFunction = (params) => () => {
  //do whatever
};

setTimeout(someFunction(params), 1000);

Ou si someFunction est le premier ordre:

setTimeout(() => someFunction(params), 1000); 
1
John Hartman

Vous pouvez essayer la fonctionnalité par défaut de 'apply ()' quelque chose comme ceci, vous pouvez passer plus d'arguments que votre exigence dans le tableau 

function postinsql(topicId)
{
  //alert(topicId);
}
setTimeout(
       postinsql.apply(window,["mytopic"])
,500);
0

// Voici trois réponses très simples et concises:

function fun() {
    console.log(this.prop1, this.prop2, this.prop3);
}

let obj = { prop1: 'one', prop2: 'two', prop3: 'three' };

let bound = fun.bind(obj);

setTimeout(bound, 3000);

 // or

function funOut(par1, par2, par3) {

  return function() { 

    console.log(par1, par2, par3);

  }
};

setTimeout(funOut('one', 'two', 'three'), 5000);

 // or

let funny = function(a, b, c) { console.log(a, b, c); };

setTimeout(funny, 2000, 'hello', 'worldly', 'people');
0
Rich

Comme il y a un problème avec le troisième paramètre optonal dans IE et que l'utilisation de fermetures nous empêche de modifier les variables (dans une boucle par exemple) et d'obtenir le résultat souhaité, je suggère la solution suivante.

Nous pouvons essayer d'utiliser la récursivité comme ceci:

var i = 0;
var hellos = ["Hello World1!", "Hello World2!", "Hello World3!", "Hello World4!", "Hello World5!"];

if(hellos.length > 0) timeout();

function timeout() {                
    document.write('<p>' + hellos[i] + '<p>');
    i++;
    if (i < hellos.length)
        setTimeout(timeout, 500);
}

Nous devons nous assurer que rien d'autre ne change ces variables et que nous écrivons une condition de récursivité appropriée pour éviter une récursion infinie.

0

Vous pouvez transmettre le paramètre à la fonction de rappel setTimeout en tant que:

setTimeout (fonction, millisecondes, param1, param2, ...)

par exemple.

function myFunction() {
  setTimeout(alertMsg, 3000, "Hello");
}

function alertMsg(message) {
    alert(message)
}
0
Jatin Verma

@ Jiri Vetyska, merci pour le message, mais il y a quelque chose qui ne va pas dans votre exemple ... Je devais passer la cible qui est survolée (ceci) à une fonction de temporisation et j'ai essayé votre approche. Testé dans IE9 - ne fonctionne pas. J'ai également fait des recherches et il apparaît que, comme indiqué ici , le troisième paramètre est le langage de script utilisé. Aucune mention sur les paramètres supplémentaires.

J'ai donc suivi la réponse de @ meder et résolu mon problème avec ce code:

$('.targetItemClass').hover(ItemHoverIn, ItemHoverOut);

function ItemHoverIn() {
 //some code here
}

function ItemHoverOut() {
    var THIS = this;
    setTimeout(
        function () { ItemHoverOut_timeout(THIS); },
        100
    );
}
function ItemHoverOut_timeout(target) {
    //do something with target which is hovered out
}

J'espère que c'est utile pour quelqu'un d'autre.

0
Vladislav