我怎样才能将一个parameter passing给setTimeout()callback?

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

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

我得到一个错误, topicId没有被定义一切工作之前,我使用setTimeout()函数。

我想要一段时间后调用postinsql(topicId)函数。 我该怎么办?

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

你需要提供一个匿名函数作为参数而不是string,后一种方法甚至不应该按ECMAScript规范工作,但浏览器只是宽松的。 这是一个合适的解决scheme,在使用setTimeout()setInterval()时,不要依赖string作为“函数”,因为它必须被评估,而且它不正确。

更新:

正如Hobblin在他对这个问题的评论中所说的,现在你可以使用Function.prototype.bind()将parameter passing给setTimeout中的Function.prototype.bind()

例:

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

在现代浏览器中,“setTimeout”接收第三个参数,该参数在定时器结束时作为参数发送给内部函数。

例:

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

经过一番研究和testing,唯一正确的实现是:

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

setTimeout将所有的额外parameter passing给你的函数,以便在那里处理。

匿名函数可以用于非常基本的东西,但是在必须使用“this”的对象实例中,无法使其工作。 任何匿名函数都会将“this”更改为指向窗口,所以您将失去对象引用。

这是一个已经“正确”回答的一个非常古老的问题,但是我想我会提到另一个没有人在这里提到的方法。 这是从优秀的下划线库复制和粘贴:

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

您可以根据需要传递setTimeout调用的函数的参数, 作为额外的奖励(通常是奖金),传递给函数的参数的值在您调用setTimeout时被冻结,所以如果它们改变了值在setTimeout()被调用的时候和超时之间的某个时间点,那么…那不是那么令人沮丧的:)

这里有一个小提琴你可以看到我的意思 – http://jsfiddle.net/thedavidmeister/7t2bV/

我最近遇到了需要在一个循环中使用setTimeout的独特情况。 了解这可以帮助您了解如何将parameter passing给setTimeout

方法1

根据Sukima的build议使用forEachObject.keys

 var testObject = { prop1: 'test1', prop2: 'test2', prop3: 'test3' }; Object.keys(testObject).forEach(function(propertyName, i) { setTimeout(function() { console.log(testObject[propertyName]); }, i * 1000); }); 

我推荐这种方法。

方法2

使用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/

方法3

或者如果你不能使用forEachbind ,使用一个IIFE :

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

方法4

但是,如果你不关心IE <10,那么你可以使用法比奥的build议 :

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

方法5(ES6)

使用块范围variables:

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

虽然我仍然推荐在ES6中使用forEach Object.keys

霍布林已经在这个问题上评论过这个问题,但这应该是一个真正的答案!

使用Function.prototype.bind()是最干净和最灵活的方式来做到这一点(与能够设置this上下文额外的好处):

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

欲了解更多信息,请参阅这些MDN链接:
https://developer.mozilla.org/zh/docs/DOM/window.setTimeout#highlighter_547041 https://developer.mozilla.org/zh/docs/JavaScript/Reference/Global_Objects/Function/bind#With_setTimeout

有些答案是正确的,但是令人费解。

四年后,我再次回答,因为我仍然遇到过分复杂的代码来解决这个问题。 有一个优雅的解决scheme。

首先,在调用setTimeout时不要传入一个string作为第一个参数,因为它实际上会调用慢速的“eval”函数。

那么我们如何将parameter passing给超时函数呢? 通过使用closures:

 settopic=function(topicid){ setTimeout(function(){ //thanks to closure, topicid is visible here postinsql(topicid); },4000); } ... if (xhr.readyState==4){ settopic(xhr.responseText); } 

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

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

语法解决。 但是在settopic被调用的时候,即4秒之后,XHR对象可能不一样。 因此, 预先绑定variables很重要。

更换

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

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

或更好,用一个匿名函数replacestringexpression式

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

编辑:

布朗斯通的评论是不正确的,这将按预期工作,如在Firebug控制台中运行所示

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

请注意,我同意别人,你应该避免传递一个stringsetTimeout因为这将调用string的eval() ,而是传递一个函数。

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

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

如果你不介意不支持IE 9或更低版本:

 setTimeout(postinsql, 4000, topicId); 

setTimeout桌面浏览器的兼容性

setTimeout手机浏览器的兼容性

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

我的答案:

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

说明:

创build的匿名函数返回另一个匿名函数。 这个函数可以访问最初传递的topicId ,所以不会出错。 第一个匿名函数被立即调用,传递给topicId ,所以带有延迟的注册函数在调用的时候可以通过闭包访问topicId

要么

这基本上转换为:

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

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

我知道这是旧的,但我想添加我(首选)的味道。

我认为一个相当可读的方法来实现这一点是将topicId传递给一个函数,该函数依次使用参数在内部引用主题ID。 即使外部的topicId很快会改变,这个值也不会改变。

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

或简称:

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

这适用于所有浏览器(IE是一个古怪的)

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

请注意,topicId是每个错误消息“未定义”的原因是,它在执行setTimeout时作为局部variables存在,而不是在postinsql的延迟调用发生时存在。 variables生命周期尤其重要,特别是在尝试像传递“this”这样的对象引用时。

我听说你可以将topicId作为第三个parameter passing给setTimeout函数。 没有太多的细节给出,但我得到足够的信息,使其工作,并在Safari成功。 我不知道他们是什么意思关于“毫秒的错误”,虽然。 看看这里:

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

我如何解决这个阶段?

就像那样:

 setTimeout((function(_deepFunction ,_deepData){ var _deepResultFunction = function _deepResultFunction(){ _deepFunction(_deepData); }; return _deepResultFunction; })(fromOuterFunction, fromOuterData ) , 1000 ); 

setTimeout等待一个函数的引用,所以我创build它在一个闭包,它解释我的数据,并返回一个函数与我的数据的好实例!

也许你可以改进这部分:

 _deepFunction(_deepData); // change to something like : _deepFunction.apply(contextFromParams , args); 

我testing了铬,火狐和IE浏览器,它执行得很好,我不知道性能,但我需要它的工作。

样品testing:

 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 

也许你可以改变签名以使​​其更加容易:

 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) } 

最后回答原来的问题:

  myNass_setTimeOut( postinsql, 4000, topicId ); 

希望它可以帮助!

ps:对不起,但是英文不是我的母语!

David Meister的回答似乎是在调用setTimeout()之后,但在匿名函数被调用之前立即改变的参数。 但是太麻烦了,不是很明显。 我发现了一个使用IIFE(即时inviked函数expression式)完成同样事情的优雅方法。

在下面的示例中, currentListvariables被传递给IIFE,将其保存在闭包中,直到调用延迟的函数。 即使在显示的代码之后variablescurrentList立即改变, setInterval()也会做正确的事情。

如果没有这个IIFE技术, setTimeout()函数肯定会被调用到DOM中的每个h2元素,但是所有这些调用只会看到最后一个 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> 

我想你想要:

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

@Jiri Vetyska谢谢你的post,但是你的例子中有什么问题。 我需要通过(这个)的目标是超时function,我试着你的方法。 在IE9testing – 不起作用。 我也做了一些研究,看来这里指出的第三个参数是脚本语言被使用。 没有提到额外的参数。

所以,我跟着@ meder的回答,并用这个代码解决了我的问题:

 $('.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 } 

希望这对别人有用。

由于IE中的第三个optonal参数存在问题,并且使用闭包来阻止我们更改variables(例如在一个循环中)并且仍然达到期望的结果,所以我build议采用以下解决scheme。

我们可以尝试使用像这样的recursion:

 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); } 

我们需要确保没有别的东西能改变这些variables,而且我们编写了一个适当的recursion条件来避免无限recursion。