如何将参数传递给setTimeout()回调?

Zee*_*ang 796 javascript parameters callback settimeout

我有一些JavaScript代码,如下所示:

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

function postinsql(topicId)
{
  //alert(topicId);
}
Run Code Online (Sandbox Code Playgroud)

我得到一个topicId未定义的错误在使用该setTimeout()函数之前,一切正常.

我希望postinsql(topicId)在一段时间后调用我的函数.我该怎么办?

med*_*iev 1083

setTimeout(function() {
    postinsql(topicId);
}, 4000)
Run Code Online (Sandbox Code Playgroud)

您需要将匿名函数作为参数而不是字符串提供,后一种方法甚至不应该按照ECMAScript规范工作,但浏览器只是宽松的.这是正确的解决方案,不要依赖于在使用时将字符串作为"函数"传递,setTimeout()或者setInterval()它更慢,因为它必须被评估并且它不正确.

更新:

正如Hobblin在对该问题的评论中所说,现在你可以将参数传递给setTimeout中的函数 Function.prototype.bind()

例:

setTimeout(postinsql.bind(null, topicId), 4000);
Run Code Online (Sandbox Code Playgroud)

  • `window.setTimeout`是一个DOM方法,因此ECMAScript规范没有定义它.传递一个字符串一直在浏览器中工作,并且是_de facto_标准 - 事实上,稍后使用JavaScript 1.2添加了传递函数对象的能力 - 它明确是HTML5草案规范的一部分(http://www.whatwg .ORG /规格/网络应用/电流工作/多页/ browsers.html浏览#GET-的定时任务).但是,使用字符串而不是函数对象通常被认为是不好的样式,因为它本质上是一种延迟的`eval()`. (29认同)
  • @pilau这正是我的问题:如果匿名函数中使用的变量在超时之前发生了变化(例如在for循环中),那么它也会在函数内部发生变化.因此,在我的示例设置中,for循环中的5个不同的超时实际上最终使用相同的变量.使用此答案时要小心! (22认同)
  • 如果在设置超时后但在调用函数之前更改了topicId会发生什么? (12认同)
  • @pilau使用另一个闭包将帮助topicId = 12; function postinsql(topicId){console.log(topicId); function setTimeOutWithClosure(topicId){setTimeout(function(){postinsql(topicId);},1000)} setTimeOutFunction(topicId); topicId = 13; (10认同)
  • var temp = setTimeout(function(){postinsql(topicId);},4000); clearTimeout(温度); ?? (2认同)
  • 甚至比“bind”方法更好,现在您可以向 [`setTimeout`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout) 函数提供参数:`setTimeout (postinsql, 4000, topicId)`。不过,不适用于 10 以下版本的 IE。 (2认同)

Fab*_*hms 694

在现代浏览器中,"setTimeout"接收第三个参数,该参数作为参数发送到计时器末尾的内部函数.

例:

var hello = "Hello World";
setTimeout(alert, 1000, hello);
Run Code Online (Sandbox Code Playgroud)

更多细节:

  • 因为它在IE的版本中不起作用仍然非常在野外. (53认同)
  • 我不确定为什么这个答案没有被选为最佳答案.使用匿名函数确实有效,但是如果你可以简单地将第三个参数传递给原来的setTimeout函数调用...为什么不呢? (52认同)
  • 到目前为止更好的答案 如果您有在"setTimeout"调用和匿名函数的实际执行之间修改参数的代码 - 匿名函数将接收修改后的值,而不是setTimeout调用时的值.例如:for(var i = 0; i <100; i ++){setTimeout(function(){console.write(i);},0); 这将记录"100"100次(在FF上测试).目前的答案有助于避免此 (25认同)
  • 这个答案实际上让我能够传递一个事件对象,其他方法没有.我已经有了匿名功能. (4认同)
  • 根据 https://developer.mozilla.org/es/docs/Web/API/WindowTimers/setTimeout,Internet Explorer 的回调参数仅在版本 &gt;=10 中受支持,请小心,因为在许多网站 ie8 和 ie9 中仍然有一些相关份额。 (2认同)

Jir*_*ska 143

在做了一些研究和测试之后,唯一正确的实现是:

setTimeout(yourFunctionReference, 4000, param1, param2, paramN);
Run Code Online (Sandbox Code Playgroud)

setTimeout会将所有额外参数传递给您的函数,以便在那里处理它们.

匿名函数可以用于非常基本的东西,但是在你必须使用"this"的对象的实例中,没有办法让它工作.任何匿名函数都会将"this"更改为指向窗口,因此您将丢失对象引用.

  • 我心里感到悲伤,我必须告知:这在互联网浏览器中不起作用.:/所有额外的参数都是未定义的. (24认同)
  • 我只是使用`var that = this; setTimeout(function(){that.foo();},1000);` (6认同)
  • 这与[Fabio](http://stackoverflow.com/a/7503942/1269037)完全相同. (4认同)
  • 这是正确的,并且是在HTML5中指定的。http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html#dom-windowtimers-setinterval (2认同)

Dav*_*ter 44

这是一个非常古老的问题,已有"正确"的答案,但我想我会提到另一种方法,没有人在这里提到过.这是从优秀的下划线库中复制和粘贴的:

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

您可以将所需的参数传递给setTimeout调用的函数,作为额外的奖励(通常是奖金),当您调用setTimeout时,传递给函数的参数值将被冻结,因此如果它们更改了值在调用setTimeout()和超时之间的某个时刻,好吧......那不再那么令人沮丧了:)

这是一个小提琴,你可以看到我的意思.

  • 这个答案确实有效,但你似乎有一些我没有的库.以下是它的小工具:而不是slice.call,使用Array.prototype.slice.call(arguments,2) (7认同)
  • @Melanie"一些图书馆"?我在答案中说它是下划线库 - http://underscorejs.org/.但是,是的,Array.prototype.slice是别名来切片到那个库里面,所以你必须自己做,如果你不使用它,好点:) (7认同)

Dav*_*ret 37

我最近遇到了特殊情况需要来使用的setTimeout循环中.理解这一点可以帮助您了解如何传递参数setTimeout.

方法1

使用forEachObject.keys根据Sukima的建议:

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

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

我推荐这种方法.

方法2

使用 bind:

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

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

方法3

或者,如果您不能使用forEachbind使用IIFE:

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

方法4

但如果你不关心IE <10,那么你可以使用Fabio的建议:

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

方法5(ES6)

使用块范围变量:

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

虽然我还是会建议使用Object.keysforEach在ES6.

  • 注意:`.bind`不会为IE8工作和低于[参考:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind#Browser_compatibility.我最终使用了Schien的解决方案:http://stackoverflow.com/a/21213723/1876899 (2认同)

dai*_*ain 22

霍布林已就这个问题对此进行了评论,但这应该是一个答案!

使用Function.prototype.bind()是最干净,最灵活的方法(能够设置this上下文的额外好处):

setTimeout(postinsql.bind(null, topicId), 4000);
Run Code Online (Sandbox Code Playgroud)

有关更多信息,请参阅以下MDN链接:
https ://developer.mozilla.org/en/docs/DOM/window.setTimeout#highlighter_547041 https://developer.mozilla.org/en/docs/JavaScript/Reference/Global_Objects/Function /绑定#With_setTimeout


Sch*_*ien 16

一些答案是正确的,但令人费解.

4年后我再次回答这个问题,因为我仍然遇到过于复杂的代码来解决这个问题.有一个优雅的解决方案.

首先,在调用setTimeout时不要传入一个字符串作为第一个参数,因为它有效地调用了对慢速"eval"函数的调用.

那么我们如何将参数传递给超时函数呢?通过使用闭包:

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

...
if (xhr.readyState==4){
  settopic(xhr.responseText);
}
Run Code Online (Sandbox Code Playgroud)

有些人建议在调用超时函数时使用匿名函数:

if (xhr.readyState==4){
  setTimeout(function(){
    settopic(xhr.responseText);
  },4000);
}
Run Code Online (Sandbox Code Playgroud)

语法解决了.但是当调用settopic时,即4秒后,XHR对象可能不一样.因此,预先绑定变量很重要.


小智 15

您可以将参数传递给 setTimeout 回调函数,如下所示:

setTimeout(function, 毫秒, param1, param2, ...)

例如。

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

function alertMsg(message) {
    alert(message)
}
Run Code Online (Sandbox Code Playgroud)

  • 是的!这应该是公认的答案。来自 MDN:https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout (2认同)

Alp*_*til 14

我知道自从提出这个问题以来已经 10 年了,但是,如果您已经滚动到这里,我认为您仍然面临一些问题。Meder Omuraliev 的解决方案是最简单的解决方案,可能对我们大多数人都有帮助,但对于那些不想有任何约束力的人来说,这里是:

  1. 使用参数设置超时
setTimeout(function(p){
//p == param1
},3000,param1);
Run Code Online (Sandbox Code Playgroud)
  1. 使用立即调用函数表达式(IIFE)
let param1 = 'demon';
setTimeout(function(p){
    // p == 'demon'
},2000,(function(){
    return param1;
})()
);
Run Code Online (Sandbox Code Playgroud)
  1. 问题的解答
function statechangedPostQuestion()
{
  //alert("statechangedPostQuestion");
  if (xmlhttp.readyState==4)
  {
    setTimeout(postinsql,4000,(function(){
        return xmlhttp.responseText;
    })());
  }
}

function postinsql(topicId)
{
  //alert(topicId);
}
Run Code Online (Sandbox Code Playgroud)


小智 10

我的答案:

setTimeout((function(topicId) {
  return function() {
    postinsql(topicId);
  };
})(topicId), 4000);
Run Code Online (Sandbox Code Playgroud)

说明:

创建的匿名函数返回另一个匿名函数.此函数可以访问最初传递的函数topicId,因此不会出错.第一个匿名函数立即被调用,传入topicId,因此具有延迟的注册函数可以topicId在调用时通过闭包访问.

要么

这基本上转换为:

setTimeout(function() {
  postinsql(topicId); // topicId inside higher scope (passed to returning function)
}, 4000);
Run Code Online (Sandbox Code Playgroud)

编辑:我看到了同样的答案,所以看看他的.但我没有偷他的答案!我只是忘了看.阅读说明,看看它是否有助于理解代码.


Rus*_*Cam 9

更换

 setTimeout("postinsql(topicId)", 4000);
Run Code Online (Sandbox Code Playgroud)

 setTimeout("postinsql(" + topicId + ")", 4000);
Run Code Online (Sandbox Code Playgroud)

或者更好的是,用匿名函数替换字符串表达式

 setTimeout(function () { postinsql(topicId); }, 4000);
Run Code Online (Sandbox Code Playgroud)

编辑:

Brownstone的评论不正确,这将按预期工作,如在Firebug控制台中运行它所证明的那样

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

请注意,我同意其他人应避免传递字符串,setTimeout因为这会调用eval()字符串而不是传递函数.

  • @brownstone:那是不对的.超时触发时将评估该字符串. (2认同)

Mic*_*ins 6

支持setTimeout中参数的最简单的跨浏览器解决方案:

setTimeout(function() {
    postinsql(topicId);
}, 4000)
Run Code Online (Sandbox Code Playgroud)

如果您不介意不支持IE 9及更低版本:

setTimeout(postinsql, 4000, topicId);
Run Code Online (Sandbox Code Playgroud)

setTimeout桌面浏览器兼容性

setTimeout移动浏览器兼容性

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