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?
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);
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:
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.
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.
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.
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
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 .
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.
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 passanttopicId
, 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.
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);
https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout
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);
cela fonctionne dans tous les navigateurs (IE est un bizarre)
setTimeout( (function(x) {
return function() {
postinsql(x);
};
})(topicId) , 4000);
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>
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!
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:
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);
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);
// 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');
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.
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)
}
@ 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.