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

我有一些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)函数在一段时间后被调用。我该怎么办?

726615 次浏览
setTimeout(function() {postinsql(topicId);}, 4000)

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

更新:

正如Hobblin在他的评论数问题中所说,现在您可以使用Function.prototype.bind()将参数传递给setTimeout中的函数。

示例:

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

替换

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

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

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

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

编辑:

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

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

请注意,我同意其他人的观点,即您应该避免将字符串传递给setTimeout,因为这将调用字符串上的eval()并传递一个函数。

我认为你想要:

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

请注意,错误消息中topicId“未定义”的原因是它在执行setTimeout时作为局部变量存在,但在延迟调用postinsql时不存在。变量生命周期尤其需要注意,尤其是在尝试将“this”作为对象引用传递时。

我听说你可以将topicId作为第三个参数传递给setTimeout函数。没有给出太多细节,但我得到了足够的信息来让它工作,它在Safari是成功的。不过,我不知道他们关于“毫秒错误”的意思。在这里查看:

在现代浏览器(即IE11及更高版本)中,“setTimeout”接收第三个参数,该参数在定时器结束时作为参数发送给内部函数。

示例:

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

更多细节:

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

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

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

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

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

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

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

这是小提琴你能明白我的意思。

@Jiri Vetyska谢谢你的帖子,但是你的例子有问题。我需要将目标悬停(this)传递给超时函数,我尝试了你的方法。在IE9中测试-不起作用。我还做了一些研究,似乎如这里所指出的,第三个参数是正在使用的脚本语言。没有提到其他参数。

所以,我遵循@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}

希望,这对别人有用。

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

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

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

有关更多信息,请参阅这些MDN链接:
https://developer.mozilla.org/en/docs/DOM/window.setTimeout#highlighter_547041https://developer.mozilla.org/en/docs/JavaScript/Reference/Global_Objects/Function/bind#With_setTimeout

我是如何解决这个阶段的?

就像这样:

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

setTimeout等待对函数的引用,所以我在闭包中创建了它,它解释我的数据并返回一个包含我数据的良好实例的函数!

也许你可以改进这部分:

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

我在chrome,Firefox和IE上测试了它,它执行得很好,我不知道性能如何,但我需要它才能正常工作。

样本测试:

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 parmetermyParamter = "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:对不起,英语不是我的母语!

我最近遇到了需要在循环中使用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);});

我推荐这个方法。

方法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,那么你可以使用Fabio的建议

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

方法5(ES6)

使用块范围的变量:

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

虽然我仍然建议在ES6中使用Object.keysforEach

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

我认为实现这一目标的一种非常易读的方法是将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中的第三个optonal参数存在问题,并且使用闭包会阻止我们更改变量(例如在循环中)并仍然实现所需的结果,我建议以下解决方案。

我们可以尝试像这样使用递归:

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

我们需要确保没有其他东西改变这些变量,并且我们编写了一个适当的递归条件来避免无限递归。

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

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

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

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

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

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

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

语法是可行的。但是当调用set主题时,即4秒后,XHR对象可能不一样。因此,预绑定变量很重要。

David Meister的答案似乎照顾到了在调用setTimeout()之后但在调用匿名函数之前可能立即更改的参数。但它太麻烦且不太明显。我发现了一种使用IIFE(立即inviked函数表达式)做几乎同样的事情的优雅方法。

在下面的示例中,currentList变量被传递给IIFE,IIFE将其保存在其闭包中,直到调用延迟函数。即使变量currentList在显示的代码之后立即更改,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>

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

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

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

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

说明:

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

这基本上转化为:

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

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

一般来说,如果你需要将函数作为带有特定参数的回调传递,你可以使用高阶函数。这在ES6中非常优雅:

const someFunction = (params) => () => {//do whatever};
setTimeout(someFunction(params), 1000);

或者如果someFunction是一阶:

setTimeout(() => someFunction(params), 1000);

你可以尝试默认功能的'应用()'类似这样的东西,你可以传递更多的参数作为你在数组中的要求

function postinsql(topicId){//alert(topicId);}setTimeout(postinsql.apply(window,["mytopic"]),500);

如果你想传递变量作为参数,让我们试试这个

如果需求是函数,var是parmas,那么试试这个

setTimeout((param1,param2) => {alert(param1 + param2);postinsql(topicId);},2000,'msg1', 'msg2')

如果需求只是变量作为参数,那么试试这个

setTimeout((param1,param2) => { alert(param1 + param2) },2000,'msg1', 'msg2')

You can try this with ES5 and ES6

这是三个非常简单和简洁的答案:

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

这是三个非常简单和简洁的答案:

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

您可以将参数传递给setTimeout回调函数:

setTimeout(函数,毫秒,参数1,参数2,…)

eg.

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

setTimeout是What WG定义的DOM的一部分。

https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html

你想要的方法是:

handle = self.setTimeout( handler [, timeout [, arguments... ] ] )

安排超时以在超时毫秒后运行处理程序。任何参数直接传递给处理程序。

setTimeout(postinsql, 4000, topicId);

显然,IE10中支持额外的参数。或者,您可以使用setTimeout(postinsql.bind(null, topicId), 4000);,但是传递额外的参数更简单,这是可取的。

历史事实:在VBScript时代,在JScript中,setTimeout的第三个参数是语言,作为字符串,默认为“JScript”,但可以选择使用“VBScript”。https://learn.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/platform-apis/aa741500(v%3Dvs.85)

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

  1. 对setTimeout使用参数
setTimeout(function(p){//p == param1},3000,param1);
  1. 立即调用函数表达式(IIFE)
let param1 = 'demon';setTimeout(function(p){// p == 'demon'},2000,(function(){return param1;})());
  1. 问题的答案
function statechangedPostQuestion(){//alert("statechangedPostQuestion");if (xmlhttp.readyState==4){setTimeout(postinsql,4000,(function(){return xmlhttp.responseText;})());}}
function postinsql(topicId){//alert(topicId);}

回答这个问题,但通过一个带有2个参数的简单加法函数。

var x = 3, y = 4;
setTimeout(function(arg1, arg2) {delayedSum(arg1, arg2);}(x, y), 1000);
function delayedSum(param1, param2) {alert(param1 + param2); // 7}

您必须像这样从setTimeOut函数调用中删除报价

setTimeout(postinsql(topicId),4000);

//Some function, with some arguments, that need to run with argumentsvar a = function a(b, c, d, e){console.log(b, c, d, e);}
//Another function, where setTimeout using for function "a", this have the same argumentsvar f = function f(b, c, d, e){ setTimeout(a.apply(this, arguments), 100);}
f(1,2,3,4); //run
//Another function, where setTimeout using for function "a", but some another arguments using, in different ordervar g = function g(b, c, d, e){ setTimeout(function(d, c, b){a.apply(this, arguments);}, 100, d, c, b);}
g(1,2,3,4);