How can I pass a parameter to a setTimeout() callback?
我有一些javascript代码看起来像:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | function statechangedPostQuestion() { //alert("statechangedPostQuestion"); if (xmlhttp.readyState==4) { var topicId = xmlhttp.responseText; setTimeout("postinsql(topicId)",4000); } } function postinsql(topicId) { //alert(topicId); } |
我得到一个错误,没有定义
我希望在一段时间后调用我的
1 2 3 | setTimeout(function() { postinsql(topicId); }, 4000) |
您需要将一个匿名函数作为参数而不是字符串,后者的方法甚至不应该按照ECMAScript规范工作,但是浏览器只是很宽容。这是正确的解决方案,在使用
更新:
正如霍布林在他对这个问题的评论中所说,现在您可以使用
例子:
1 | setTimeout(postinsql.bind(null, topicId), 4000); |
在现代浏览器中,"settimeout"接收第三个参数,该参数作为参数发送到计时器末尾的内部函数。
例子:
1 2 | var hello ="Hello World"; setTimeout(alert, 1000, hello); |
更多细节:
- https://developer.mozilla.org/en-us/docs/web/api/windowtimers.settimeout
- http://arguments.calele.info/2008/11/10/passing-arguments-to-settimeout-and-setinterval/
在做了一些研究和测试之后,唯一正确的实现是:
1 | setTimeout(yourFunctionReference, 4000, param1, param2, paramN); |
setTimeout将把所有额外的参数传递给函数,以便在那里进行处理。
匿名函数可以用于非常基本的东西,但是在必须使用"this"的对象实例中,没有办法使它工作。任何匿名函数都会将"this"更改为指向窗口,因此您将丢失对象引用。
这是一个非常古老的问题,有一个已经"正确"的答案,但我想我会提到另一种方法,这里没有人提到过。这是从优秀的下划线库中复制和粘贴的:
1 2 3 4 | _.delay = function(func, wait) { var args = slice.call(arguments, 2); return setTimeout(function(){ return func.apply(null, args); }, wait); }; |
您可以向setTimeout调用的函数传递尽可能多的参数,作为额外的奖励(通常是奖励),当您调用setTimeout时,传递给函数的参数值将被冻结,因此,如果它们在调用setTimeout()和超时之间的某个时间点改变值,那么……这已经不再令人沮丧了。)
这是一把小提琴,你能明白我的意思。
我最近遇到了一个独特的情况,需要在一个循环中使用
方法1
根据Sukima的建议,使用
1 2 3 4 5 6 7 8 9 10 11 | var testObject = { prop1: 'test1', prop2: 'test2', prop3: 'test3' }; Object.keys(testObject).forEach(function(propertyName, i) { setTimeout(function() { console.log(testObject[propertyName]); }, i * 1000); }); |
我推荐这种方法。
方法2
使用
1 2 3 4 5 6 | var i = 0; for (var propertyName in testObject) { setTimeout(function(propertyName) { console.log(testObject[propertyName]); }.bind(this, propertyName), i++ * 1000); } |
jfiddle:http://jsfiddle.net/msbkw/
方法3
或者如果您不能使用
1 2 3 4 5 6 7 8 | var i = 0; for (var propertyName in testObject) { setTimeout((function(propertyName) { return function() { console.log(testObject[propertyName]); }; })(propertyName), i++ * 1000); } |
方法4
但是如果你不在乎IE<10,那么你可以使用法比奥的建议:
1 2 3 4 5 6 | var i = 0; for (var propertyName in testObject) { setTimeout(function(propertyName) { console.log(testObject[propertyName]); }, i++ * 1000, propertyName); } |
方法5(ES6)
使用块范围变量:
1 2 3 4 | let i = 0; for (let propertyName in testObject) { setTimeout(() => console.log(testObject[propertyName]), i++ * 1000); } |
尽管我仍然建议在ES6中使用
霍布林已经对这个问题发表了评论,但这应该是一个真正的答案!
使用
1 | setTimeout(postinsql.bind(null, topicId), 4000); |
有关详细信息,请参阅以下MDN链接:https://developer.mozilla.org/en/docs/dom/window.settimeout荧光笔https://developer.mozilla.org/en/docs/javascript/reference/global_objects/function/bind_with_settimeout
有些答案是正确的,但很复杂。
4年后,我再次回答这个问题,因为我仍然会遇到过于复杂的代码来精确地解决这个问题。有一个优雅的解决方案。
首先,在调用setTimeout时不要将字符串作为第一个参数传入,因为它有效地调用了对慢速"eval"函数的调用。
那么,我们如何将参数传递给一个超时函数呢?通过使用闭包:
1 2 3 4 5 6 7 8 9 10 11 | settopic=function(topicid){ setTimeout(function(){ //thanks to closure, topicid is visible here postinsql(topicid); },4000); } ... if (xhr.readyState==4){ settopic(xhr.responseText); } |
有些人建议在调用超时函数时使用匿名函数:
1 2 3 4 5 | if (xhr.readyState==4){ setTimeout(function(){ settopic(xhr.responseText); },4000); } |
语法正确。但是当调用settopic时,即4秒后,xhr对象可能不相同。因此,预先绑定变量非常重要。
我的回答是:
1 2 3 4 5 | setTimeout((function(topicId) { return function() { postinsql(topicId); }; })(topicId), 4000); |
说明:
The anonymous function created returns another anonymous function. This function has access to the originally passed
topicId , so it will not make an error. The first anonymous function is immediately called, passing intopicId , so the registered function with a delay has access totopicId at the time of calling, through closures.
或
这基本上转化为:
1 2 3 | setTimeout(function() { postinsql(topicId); // topicId inside higher scope (passed to returning function) }, 4000); |
编辑:我看到了同样的答案,所以看看他的。但我没有偷他的答案!我只是忘了看。阅读解释,看看它是否有助于理解代码。
替换
1 | setTimeout("postinsql(topicId)", 4000); |
具有
1 | setTimeout("postinsql(" + topicId +")", 4000); |
或者更好的是,用匿名函数替换字符串表达式
1 | setTimeout(function () { postinsql(topicId); }, 4000); |
编辑:
Brownstone的注释不正确,这将按预期工作,如在Firebug控制台中运行它所示。
1 2 3 4 5 6 7 | (function() { function postinsql(id) { console.log(id); } var topicId = 3 window.setTimeout("postinsql(" + topicId +")",4000); // outputs 3 after 4 seconds })(); |
请注意,我同意其他人的观点,您应该避免将字符串传递给
最简单的跨浏览器解决方案,支持设置超时中的参数:
1 2 3 | setTimeout(function() { postinsql(topicId); }, 4000) |
如果您不介意不支持IE 9及更低版本:
1 | setTimeout(postinsql, 4000, topicId); |
https://developer.mozilla.org/en-us/docs/web/api/windowtimers/settimeout
我知道它很老,但我想加上我喜欢的味道。
我认为实现这一点的一个非常易读的方法是将
1 2 3 4 5 6 7 | var topicId = xmlhttp.responseText; var fDelayed = function(tid) { return function() { postinsql(tid); }; } setTimeout(fDelayed(topicId),4000); |
或短:
1 2 3 4 | var topicId = xmlhttp.responseText; setTimeout(function(tid) { return function() { postinsql(tid); }; }(topicId), 4000); |
DavidMeister给出的答案似乎考虑了在调用setTimeout()之后,但在调用匿名函数之前可能立即更改的参数。但它太笨重,不太明显。我发现了一种使用IIFE(立即被邀请的函数表达式)做几乎相同事情的优雅方法。
在下面的示例中,
如果没有这种IIFE技术,将绝对为DOM中的每个
1 2 3 4 5 6 7 8 9 10 11 12 13 | // 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); }); |
这在所有浏览器中都有效(即是个奇怪的东西)
1 2 3 4 5 | setTimeout( (function(x) { return function() { postinsql(x); }; })(topicId) , 4000); |
我是如何解决这个阶段的?
就像这样:
1 2 3 4 5 6 | setTimeout((function(_deepFunction ,_deepData){ var _deepResultFunction = function _deepResultFunction(){ _deepFunction(_deepData); }; return _deepResultFunction; })(fromOuterFunction, fromOuterData ) , 1000 ); |
setTimeout等待对一个函数的引用,所以我在一个闭包中创建了它,这个闭包解释了我的数据,并返回了一个具有良好数据实例的函数!
也许你可以改进这部分:
1 2 3 4 | _deepFunction(_deepData); // change to something like : _deepFunction.apply(contextFromParams , args); |
我在Chrome、Firefox和IE上测试过它,它执行得很好,我不知道性能如何,但我需要它来工作。
样本测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | 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 |
也许您可以更改签名以使其更具兼容性:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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) } |
最后回答最初的问题:
1 | myNass_setTimeOut( postinsql, 4000, topicId ); |
希望能有所帮助!
附言:对不起,但英语不是我的母语!
一般来说,如果需要使用特定参数将函数作为回调传递,则可以使用高阶函数。ES6非常优雅:
1 2 3 4 5 | const someFunction = (params) => () => { //do whatever }; setTimeout(someFunction(params), 1000); |
或者,如果
1 | setTimeout(() => someFunction(params), 1000); |
如果要将变量作为参数传递,请尝试此操作
如果需求是函数,var是参数,那么试试这个。
1 2 3 4 | setTimeout((param1,param2) => { alert(param1 + param2); postinsql(topicId); },2000,'msg1', 'msg2') |
如果需求只是作为参数的变量,请尝试此操作
1 | setTimeout((param1,param2) => { alert(param1 + param2) },2000,'msg1', 'msg2') |
你可以用ES5和ES6试试这个
请注意,根据错误消息,"未定义"topicid的原因是,在执行setTimeout时它作为局部变量存在,但在对postinsql的延迟调用发生时不存在。变量生存期特别重要,尤其是在尝试将"this"作为对象引用传递时。
我听说您可以将topicid作为第三个参数传递给setTimeout函数。虽然没有提供太多细节,但我有足够的信息让它发挥作用,而且它在狩猎中很成功。我不知道"毫秒误差"是什么意思。在这里查看:
http://www.howtocreate.co.uk/tutorials/javascript/timers
您可以尝试"apply()"的默认功能,类似于这样,您可以在数组中传递更多的参数作为您的要求。
1 2 3 4 5 6 7 | function postinsql(topicId) { //alert(topicId); } setTimeout( postinsql.apply(window,["mytopic"]) ,500); |
//这是三个非常简单简洁的答案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | 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'); |
@JiriVetyska感谢你的帖子,但是你的例子有点问题。我需要将悬停的目标(这个)传递到一个超时函数,我尝试了您的方法。在IE9中测试-不起作用。我也做了一些研究,正如这里指出的,第三个参数是正在使用的脚本语言。没有提到其他参数。
因此,我按照@meder的答案,用以下代码解决了我的问题:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | $('.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中的第三个视觉参数存在问题,并且使用闭包会阻止我们更改变量(例如在循环中),并仍然达到预期的结果,因此我建议采用以下解决方案。
我们可以尝试使用如下递归:
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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); } |
我们需要确保没有其他东西改变这些变量,并且我们编写一个适当的递归条件来避免无限递归。
我想你想要:
1 | setTimeout("postinsql(" + topicId +")", 4000); |
//这是三个非常简单简洁的答案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | 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'); |