我怎样才能将一个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议使用forEach
和Object.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
或者如果你不能使用forEach
或bind
,使用一个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);
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成功。 我不知道他们是什么意思关于“毫秒的错误”,虽然。 看看这里:
我如何解决这个阶段?
就像那样:
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式)完成同样事情的优雅方法。
在下面的示例中, currentList
variables被传递给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。