967 votes

Comment puis-je passer un paramètre à un callback de 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 une erreur selon laquelle topicId n'est pas défini. Tout fonctionnait avant que j'utilise la fonction setTimeout().

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

92 votes

Cela fait un peu mal de commenter un sujet aussi ancien mais je dois juste offrir une troisième version (qui, à mon avis, est beaucoup plus propre) : setTimeout(postinsql.bind(null, topicId), 4000)

3 votes

1254voto

meder Points 81864
setTimeout(function() {
    postinsql(topicId);
}, 4000);

Vous devez alimenter une fonction anonyme en tant que paramètre au lieu d'une chaîne de caractères. Cette dernière méthode ne devrait même pas fonctionner - selon la spécification ECMAScript - mais les navigateurs sont simplement indulgents. Ceci est la solution appropriée. Ne vous fiez jamais à passer une chaîne de caractères en tant que 'fonction' lors de l'utilisation de setTimeout() ou setInterval(). C'est plus lent, car cela doit être évalué et ce n'est tout simplement pas correct.

MISE À JOUR:

Comme l'a dit Hobblin 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);

0 votes

Je me souviens qu'il m'a fallu du temps pour arriver à cette conclusion. Parfois, à l'intérieur des objets, je pense que l'appel régulier au livre avec '' ne fonctionne pas. Je ne sais toujours pas exactement pourquoi,,, mais c'était aussi ma solution.

31 votes

window.setTimeout est une méthode du DOM, et en tant que telle n'est pas définie par la spécification ECMAScript. Passer une chaîne a toujours fonctionné dans les navigateurs, et est une norme de facto - en fait, la capacité de passer un objet fonction a été ajoutée plus tard, avec JavaScript 1.2 - elle fait explicitement partie de l'ébauche de spécification HTML5 (whatwg.org/specs/web-apps/current-work/multipage/…). Cependant, l'utilisation d'une chaîne au lieu d'un objet fonction est généralement considérée comme de mauvais style car c'est essentiellement une forme d' eval() différé.

1 votes

Eck - ma mémoire ne me sert pas bien aujourd'hui .. setTimeout n'est pas défini par ES oui, mais je ne pense pas que ce soit une méthode DOM officielle que vous trouverez dans la spécification, juste une méthode DOM 0 (prise en charge par les navigateurs depuis les premiers jours).

864voto

Fabio Phms Points 1725

Dans les navigateurs modernes (c'est-à-dire IE11 et au-delà), le "setTimeout" reçoit un troisième paramètre qui est envoyé en tant que paramètre à la fonction interne à la fin du minuteur.

Exemple:

var hello = "Bonjour le monde";
setTimeout(alert, 1000, hello);

Plus de détails sur fonction globale setTimeout() - MDN

68 votes

Je ne suis pas sûr pourquoi cette réponse n'a pas été sélectionnée comme la meilleure. Utiliser une fonction anonyme fonctionne, c'est sûr, mais si vous pouvez simplement passer un troisième paramètre dans l'appel de la fonction setTimeout original... pourquoi pas ?

57 votes

Parce que cela ne fonctionne pas dans les versions d'IE toujours très présentes.

0 votes

Il semble également que ce ne soit pas une partie officielle de la spécification. De plus, w3cschools a le troisième paramètre comme langue pour exécuter la fonction.

186voto

Jiri Vetyska Points 489

Après avoir fait des recherches et des tests, la seule implémentation correcte est :

setTimeout(votreRéférenceDeFonction, 4000, param1, param2, paramN);

setTimeout transmettra tous les paramètres supplémentaires à votre fonction pour qu'ils puissent être traités là-bas.

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

26 votes

C'est avec tristesse dans mon cœur que je dois informer : cela ne fonctionne pas dans Internet Explorer. :/ Tous les paramètres supplémentaires sont transmis comme indéfinis.

0 votes

"au sein d'une instance d'un objet où vous devez utiliser "this", il n'y a aucun moyen de le faire fonctionner." - pas vrai. Vous pouvez faire fonctionner la référence this, même sans utiliser bind (pris en charge à partir d'IE9). Il vous suffit de passer la fonction anonyme suivante : function() { someObject.someMethod() }.

8 votes

Je viens d'utiliser var that = this; setTimeout( function() { that.foo(); }, 1000);

47voto

David Meister Points 958

C'est une question très ancienne avec déjà une réponse "correcte", mais je pensais mentionner une autre approche que personne n'a mentionnée ici. C'est copié-collé de l'excellente bibliothèque underscore:

_.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 le souhaitez à la fonction appelée par setTimeout et en prime (enfin, habituellement en prime), la valeur des arguments passés à votre fonction est figée lorsque vous appelez setTimeout, donc s'ils changent de valeur à un moment donné entre l'appel de setTimeout() et l'expiration du délai, eh bien... ce n'est plus si frustrant que ça :)

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

8 votes

Cette réponse fonctionne effectivement mais il semble que vous disposiez d'une bibliothèque que je n'ai pas. Voici la petite correction pour faire fonctionner le code : au lieu de slice.call, utilisez Array.prototype.slice.call(arguments, 2)

7 votes

@Melanie "some library"? J'ai dit dans la réponse que c'est la bibliothèque underscore - underscorejs.org. Mais oui, Array.prototype.slice est un alias pour slice à l'intérieur de cette bibliothèque, donc vous devez le faire vous-même si vous ne l'utilisez pas, bien repéré :)

42voto

David Sherret Points 3205

J'ai récemment été confronté à la situation unique de devoir utiliser un setTimeout dans une boucle. Comprendre cela peut vous aider à comprendre comment passer des paramètres à setTimeout.

Méthode 1

Utilisez forEach et Object.keys, comme le suggère Sukima dans sa suggestion :

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 une 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 pourriez 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 à portée de bloc :

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

Cependant, je vous recommande toujours d'utiliser Object.keys avec forEach en ES6.

2 votes

Note: .bind ne fonctionnera pas pour IE8 et inférieur [réf: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… ]. J'ai fini par utiliser la solution de Schien: stackoverflow.com/a/21213723/1876899

1 votes

Si votre environnement utilise bind, alors vous êtes également dans un environnement qui offre Object.keys et forEach. Vous pouvez vous passer de la boucle for et bénéficier d'une portée de fonction "gratuite" (comme dans "faire d'une pierre deux coups" et non "gratuit en ressources") dans le processus.

0 votes

@David Sherret, au cas où vous ne l'auriez pas encore utilisé, consultez certainement la bibliothèque async (github.com/caolan/async). Nous l'utilisons largement dans Sails et avons obtenu d'excellents résultats au cours des 2 dernières années. Elle propose des méthodes à la fois en parallèle et en série pour des opérations asynchrones telles que forEach, map, reduce, etc.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X