Pregunta ¿Cómo puedo pasar un parámetro a una devolución de llamada setTimeout ()?


Tengo un código JavaScript que se ve así:

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

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

Me sale un error que topicId no está definido Todo estaba funcionando antes de usar el setTimeout() función.

Quiero mi postinsql(topicId) función para llamar después de un tiempo. ¿Que debería hacer?


676
2017-07-27 21:13


origen


Respuestas:


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

Necesita alimentar una función anónima como un parámetro en lugar de una cadena, este último método ni siquiera debería funcionar según la especificación ECMAScript, pero los navegadores son indulgentes. Esta es la solución adecuada, nunca confíe en pasar una cuerda como una 'función' cuando use setTimeout() o setInterval(), es más lento porque tiene que ser evaluado y simplemente no es correcto.

ACTUALIZAR:

Como dijo Hobblin en sus comentarios a la pregunta, ahora puedes pasar argumentos a la función dentro de setTimeout usando Function.prototype.bind()

Ejemplo:

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

942
2017-07-27 21:15



En los navegadores modernos, el "setTimeout" recibe un tercer parámetro que se envía como parámetro a la función interna al final del temporizador.

Ejemplo:

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

Más detalles:


569
2017-09-21 17:23



Después de hacer algunas investigaciones y pruebas, la única implementación correcta es:

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

setTimeout pasará todos los parámetros adicionales a su función para que puedan procesarse allí.

La función anónima puede funcionar para cosas muy básicas, pero en el caso de un objeto donde tiene que usar "esto", no hay forma de hacerlo funcionar. Cualquier función anónima cambiará "esto" para apuntar a la ventana, por lo que perderá su referencia de objeto.


114
2017-10-30 22:08



Esta es una pregunta muy antigua con una respuesta ya "correcta", pero pensé que mencionaría otro enfoque que nadie ha mencionado aquí. Esto se copia y pega de la excelente biblioteca de guiones bajos:

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

Puede pasar tantos argumentos como quiera a la función llamada por setTimeout y como una ventaja adicional (bueno, normalmente una bonificación), el valor de los argumentos pasados ​​a su función se congela cuando llama a setTimeout, por lo que si cambian de valor en algún momento entre cuando se llama a setTimeout () y se agota, bueno. .. eso ya no es tan horriblemente frustrante :)

Aquí hay un violín donde puedes ver lo que quiero decir: http://jsfiddle.net/thedavidmeister/7t2bV/


43
2017-12-18 14:28



Recientemente me encontré con la situación única de necesitar usar un setTimeout en un lazo. Comprender esto puede ayudarlo a entender cómo pasar parámetros a setTimeout.

Método 1

Utilizar forEach y Object.keys, según Sukima sugerencia:

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

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

Recomiendo este método

Método 2

Utilizar 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étodo 3

O si no puedes usar forEach o bind, usa un IIFE:

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

Método 4

Pero si no te importa IE <10, entonces podrías usar Fabio's sugerencia:

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

Método 5 (ES6)

Use una variable de ámbito de bloque:

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

Aunque todavía recomendaría usar Object.keyscon forEach en ES6.


34
2017-07-09 19:58



Hobblin ya comentó esto sobre la pregunta, ¡pero debería ser una respuesta realmente!

Utilizando Function.prototype.bind() es la forma más limpia y flexible de hacerlo (con la ventaja añadida de poder configurar el this contexto):

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

Para obtener más información, consulte estos enlaces de MDN:
https://developer.mozilla.org/en/docs/DOM/window.setTimeout#highlighter_547041 https://developer.mozilla.org/en/docs/JavaScript/Reference/Global_Objects/Function/bind#With_setTimeout


22
2018-03-25 16:34



Algunas respuestas son correctas pero intrincadas.

Estoy respondiendo esto de nuevo, 4 años después, porque todavía encuentro un código demasiado complejo para resolver exactamente esta pregunta. HAY una solución elegante.

En primer lugar, no transfiera una cadena como primer parámetro al llamar a setTimeout porque invoca de manera efectiva una llamada a la función lenta "eval".

Entonces, ¿cómo pasamos un parámetro a una función de tiempo de espera? Al usar el cierre:

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

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

Algunos han sugerido usar la función anónima al llamar a la función de tiempo de espera:

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

La sintaxis funciona. Pero cuando se llama a Settopic, es decir, 4 segundos después, el objeto XHR puede no ser el mismo. Por lo tanto, es importante vincular previamente las variables.


13
2018-01-19 06:00



Reemplazar

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

con

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

o mejor aún, reemplace la expresión de cadena con una función anónima

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

EDITAR:

El comentario de Brownstone es incorrecto, esto funcionará según lo previsto, como se demuestra al ejecutar esto en la consola Firebug

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

Tenga en cuenta que estoy de acuerdo con los demás en que debe evitar pasar una cuerda a setTimeout como esto llamará eval() en la cadena y en su lugar pasar una función.


9
2017-07-27 21:16



Mi respuesta:

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

Explicación:

La función anónima creada devuelve otra función anónima. Esta función tiene acceso al aprobado originalmente topicId, por lo que no hará un error. La primera función anónima se llama inmediatamente, pasando topicId, por lo que la función registrada con un retraso tiene acceso a topicId en el momento de la llamada, a través de cierres.

O

Esto básicamente se convierte en:

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

EDIT: Vi la misma respuesta, así que mira la suya. ¡Pero no robé su respuesta! Me olvidé de mirar. Lea la explicación y vea si le ayuda a entender el código.


7
2018-06-01 03:27



La solución más fácil de navegador cruzado para soportar parámetros en setTimeout:

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

Si no te importa no admitir IE 9 y versiones anteriores:

setTimeout(postinsql, 4000, topicId);

setTimeout desktop browser compatibility

setTimeout mobile browser compatibility

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


5
2017-11-20 11:07



Sé que es viejo, pero quería agregar mi sabor (preferido) a esto.

Creo que una forma bastante fácil de lograr esto es pasar el topicId a una función, que a su vez usa el argumento para hacer referencia interna al ID del tema. Este valor no cambiará aunque topicId en el exterior se cambiará poco después.

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

o corto:

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

3
2018-06-19 12:45