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

J'ai un 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 une erreur 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?

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

Vous devez alimenter une fonction anonyme en tant que paramètre au lieu d'une chaîne, la dernière méthode ne devrait même pas fonctionner selon la spécification ECMAScript, mais les navigateurs ne sont que clairs. C'est la solution appropriée, ne comptez jamais sur le passage d'une chaîne en tant que «fonction» lors de l'utilisation de setTimeout() ou setInterval() , elle est plus lente car elle doit être évaluée et ce n'est pas juste.

METTRE À JOUR:

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

Exemple:

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

Dans les navigateurs modernes, le "setTimeout" reçoit un troisième paramètre qui est envoyé comme paramètre à la fonction interne à la fin de la minuterie.

Exemple:

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

Après quelques recherches et essais, la seule mise en œuvre correcte est la suivante:

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

SetTimeout passera tous les paramètres supplémentaires à votre fonction afin qu'ils puissent être traités là-bas.

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

C'est une très ancienne question avec une réponse déjà «correcte», mais j'ai pensé que je parlerais d'une autre approche que personne n'a mentionnée ici. Ceci est copié et collé à partir de l'excellente bibliothèque de soulignement:

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

Vous pouvez passer autant d'arguments que vous souhaitez pour la fonction appelée par setTimeout et en bonus supplémentaire (bien, généralement un bonus), la valeur des arguments passés à votre fonction est gelée lorsque vous appelez setTimeout, donc si elles changent de valeur À un certain moment entre quand setTimeout () est appelé et quand il éteint, bien … ce n'est pas si horriblement frustrant plus 🙂

Voici un violon où vous pouvez voir ce que je veux dire – http://jsfiddle.net/thedavidmeister/7t2bV/

J'ai récemment rencontré la situation unique de la nécessité d'utiliser un setTimeout en boucle . Comprendre cela peut vous aider à comprendre comment passer les paramètres à setTimeout .

Méthode 1

Utilisez pour forEach et Object.keys , selon 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 pour forEach ou pour 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, alors vous pouvez utiliser la suggestion de Fabio:

 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 toujours d'utiliser Object.keys avec forEach dans ES6.

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

L'utilisation de Function.prototype.bind() est la manière la plus propre et la plus souple de le faire (avec le bonus supplémentaire de pouvoir configurer this contexte):

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

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

Certaines réponses sont correctes mais compliquées.

Je réponds encore une fois, 4 ans plus tard, parce que je suis encore confronté à un code trop complexe pour résoudre exactement cette question. Il y a une solution élégante.

Tout d'abord, ne passez pas une chaîne comme premier paramètre lorsque vous appelez setTimeout car elle appelle effectivement un appel à la fonction "eval" lente.

Alors, comment passons-nous un paramètre à une fonction de temporisation? 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 temporisation:

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

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

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 cela a été démontré en l'exécutant 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 que vous devriez éviter de passer une chaîne à setTimeout car cela appellera eval() sur la chaîne et au lieu de passer une fonction.

La solution de navigateur croisée la plus simple pour supporter les paramètres dans setTimeout:

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

Si cela ne vous dérange pas de ne pas supporter IE 9 et plus bas:

 setTimeout(postinsql, 4000, topicId); 

Compatibilité du navigateur de bureau SetTimeout

Compatibilité de navigateur mobile SetTimeout

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

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 au topicId initialement passé, donc il ne va pas faire d'erreur. La première fonction anonyme est immédiatement appelée, en passant par topicId , de sorte que la fonction enregistrée avec un délai a accès au topicId au moment de l'appel, par des fermetures.

OU

Ceci se convertit essentiellement en:

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

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

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

Je pense qu'une manière assez lisible d'y parvenir est de passer le topicId à une fonction qui, à son tour, utilise l'argument pour faire référence à l'ID du sujet en interne. Cette valeur ne changera pas même si le topicId à l'extérieur sera changé 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); 

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

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

Notez que la raison pour laquelle topicId était "non défini" par le message d'erreur, c'est qu'il existait en tant que variable locale lorsque le setTimeout a été exécuté, mais pas lorsque l'appel retardé au postinsql s'est produit. La durée de vie variable est particulièrement importante pour faire attention, surtout lorsqu'on tente quelque chose comme le passage de «ceci» comme référence d'objet.

J'ai entendu dire que vous pouvez passer topicId comme un troisième paramètre à la fonction setTimeout. Il n'y a pas beaucoup de détails, mais j'ai eu suffisamment d'informations pour le faire fonctionner, et c'est un succès dans Safari. Je ne sais pas ce qu'ils veulent dire de l'erreur de milliseconde. Vérifiez le ici:

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

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, donc je l'ai créé 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 fonctionne bien, je ne connais pas les performances, mais j'avais besoin de fonctionner.

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 compliquée:

 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 finalement pour répondre à la question initiale:

  myNass_setTimeOut( postinsql, 4000, topicId ); 

J'espère que cela peut vous aider!

Ps: désolé mais l'anglais, ce n'est pas ma langue maternelle!

La réponse de David Meister semble prendre soin des paramètres qui peuvent changer immédiatement après l'appel à setTimeout () mais avant que la fonction anonyme ne soit appelée. 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 en utilisant IIFE (expression de fonction immédiatement invoquée).

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

Sans cette technique IIFE, la fonction setTimeout() sera définitivement appelée pour chaque élément h2 dans le DOM, mais tous ces appels ne verront que la valeur du texte du dernier élément 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> 

Je pense que vous voulez:

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

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

Alors, j'ai suivi la réponse de @ meder et j'ai 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 cela est utile pour quelqu'un d'autre.

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

Nous pouvons essayer d'utiliser la récurrence 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 modifie ces variables et que nous écrivons une bonne condition de récurrence pour éviter une récurrence infinie.