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


825

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


75
在这样一个古老的话题上发表评论会有点伤心,但是我只需要提供第三个版本(在我看来这是更干净的):setTimeout(postinsql.bind(null,topicId),4000)
Hobblin 2011年

14
@霍布林:为什么打破纪录会很痛苦?着眼于这个受欢迎的问题并看到过时的,不合理的通话方式,是否会进一步伤害其他人?
Dan Dascalescu 2015年


@dbliss我的建议已在多个答案中提及,并在此处添加为答案:stackoverflow.com/a/15620038/287130 但我很高兴我的建议受到赞赏;)
Hobblin

@Hobblin啊,非常感谢您指出这一点。我一直在寻找您发布的答案,却错过了。
dbliss

Answers:


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

您需要将匿名函数作为参数而不是字符串作为参数,后一种方法甚至不符合ECMAScript规范,但浏览器比较宽松。这是正确的解决方案,在使用setTimeout()或时setInterval(),永远不要依赖于将字符串作为“函数”进行传递,因为它必须进行求值并且不正确,所以速度较慢。

更新:

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

例:

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

30
window.setTimeout是DOM方法,因此ECMAScript规范未定义。传递字符串在浏览器中一直有效,实际上是一种标准-实际上,后来在JavaScript 1.2中添加了传递函数对象的功能-它显然是HTML5规范草案的一部分(whatwg.org/specs/web -apps / current-work / multipage /…)。但是,使用字符串代替函数对象通常被认为是较差的样式,因为它实质上是delay的一种形式eval()
英里

2
var temp = setTimeout(function(){postinsql(topicId);},4000); clearTimeout(temp); ??
2012年

12
如果在设置超时后但在调用函数之前更改topicId,会发生什么情况?
pilau 2012年

22
@pilau正是我的问题:如果匿名函数中使用的变量在超时之前更改(例如在for循环中),那么它也将在函数内部更改。因此,在我的示例中,在for循环中设置5个不同的超时实际上最终使用了相同的变量。使用此答案时要小心!
克里斯蒂安

10
@pilau使用另一个闭包将有助于topicId = 12; 函数postinsql(topicId){console.log(topicId); }函数setTimeOutWithClosure(topicId){setTimeout(function(){postinsql(topicId);},1000)} setTimeOutFunction(topicId); topicId = 13;
哈利斯Yılboğa2013年

727

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

例:

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

更多细节:


56
我不确定为什么未选择此答案为最佳答案。当然可以使用匿名函数,但是如果您可以简单地将第三个参数传递给原始setTimeout函数调用...为什么不呢?
克里斯·舒乌

54
因为它在IE版本中仍然无法正常工作。
亚伦

4
这个答案实际上使我能够传递一个事件对象,而其他方法则没有。我已经有一个匿名功能。
Glenn Plas 2012年

27
到目前为止,更好的答案。如果您有代码在“ setTimeout”调用和匿名函数的实际执行之间修改参数,则匿名函数将收到修改后的值,而不是setTimeout调用时的值。例如:for(var i = 0; i <100; i ++){setTimeout(function(){console.write(i);},0); }这将记录100次“ 100”(在FF上测试)。当前答案有助于避免这种情况。

1
根据developer.mozilla.org/es/docs/Web/API/WindowTimers/setTimeout ,仅在> = 10版本中支持Internet Explorer的回调参数,请谨慎,因为在许多站点ie8和ie9仍会获得一些相关的份额。
le0diaz

153

经过研究和测试之后,唯一正确的实现是:

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

setTimeout会将所有其他参数传递给您的函数,以便可以在此处对其进行处理。

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


24
我必须悲伤地告诉我:这在Internet Explorer中不起作用。:/所有其他参数都以未定义的形式出现。
Amalgovinus

6
我只是使用var that = this; setTimeout( function() { that.foo(); }, 1000);
Ed Williams

2
这是正确的,并且是在HTML5中指定的。 whatwg.org/specs/web-apps/current-work/multipage/…–
Garrett

4
这与法比奥的答案完全相同。
Dan Dascalescu 2015年

1
根据developer.mozilla.org/es/docs/Web/API/WindowTimers/setTimeout ,仅在> = 10版本中支持Internet Explorer的回调参数,请谨慎,因为在许多站点ie8和ie9仍会获得一些相关的份额。
le0diaz

45

这是一个非常老的问题,答案已经是“正确的”,但我想我要提到的是这里没有人提及的另一种方法。这是从出色的下划线库复制并粘贴的:

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

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

这是一个小提琴,在这里您可以了解我的意思。


7
该答案实际上有效,但是您似乎拥有一些我没有的图书馆。这是它起作用的一点修正:代替slice.call,使用Array.prototype.slice.call(arguments,2)
Melanie

7
@梅兰妮“一些图书馆”?我在回答中说这是下划线库-underscorejs.org。但是,是的,Array.prototype.slice是在该库中切片的别名,因此,如果您不使用它,则必须自己做,好地方:)
David Meister 2013年

38

我最近遇到需要使用的独特形势下应运而生setTimeout的一。了解这一点可以帮助您了解如何将参数传递给setTimeout

方法一

根据Sukima的建议,使用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);
});

我推荐这种方法。

方法二

用途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.keyswith forEach


2
注意:.bind不适用于IE8及以下版本[ref:developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… ]。我最终使用了Schien的解决方案:stackoverflow.com/a/21213723/1876899
cjspurgeon 2014年

1
如果您在使用环境中,bind那么您也在提供Object.keys和的环境中forEach。您可以松开for循环,并在此过程中获得“自由”(如两只鸟,一只石头自由而不是没有资源)。
Sukima 2014年

@David Sherret,以防万一您以前从未使用过它,请务必查看该async库(github.com/caolan/async)。我们在Sails中广泛使用它,并且在过去两年中取得了不错的成绩。它提供了在平行和在异步系列方法forEachmapreduce
mikermcneil

25

霍布林(Hobblin)已经对这个问题发表了评论,但这确实是一个答案!

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

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

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


可以使用bind的第一个参数传递此上下文setTimeout(postinsql.bind(this, topicId), 4000);
Giuseppe Galano 2013年

@GiuseppeGalano完全,我在回答中提到了这一点,但此示例不需要它:)
dain 2013年

令人着迷的是在局部应用中有多少光泽bind。它确实使一些可读的代码成为可能。
Sukima 2014年

IE9 +仅支持bind(),因此该方法不适用于<IE9
Sanjeev,2014年

@Sanjeev使用ES5匀场使其在较旧的IE中运行:github.com/es-shims/es5-shim
gregers

17

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

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

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

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

语法可行。但是,在调用settopic的时间(即4秒钟后)时,XHR对象可能不相同。因此,预先绑定变量很重要。


1
+1是可读的解决方案,与我的稍有不同。在settopic函数内部有setTimeout的同时,在setTimeout函数内部有fDelayed(您的settopic)函数。
Dominic

11

我的答案:

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

说明:

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

要么

这基本上可以转换为:

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

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


这是最好的答案。这些解决方案中的很多甚至都无法解决超时问题。但是,为什么要在括号内包装第一个匿名函数?我认为完成这些任务不是必需的。
罗伯特·亨德森'18

这是最好的答案,但必须进行克隆,因为当值之类topicId的值更改时,超时值也会更改。一个克隆修复了它
pariola

10

更换

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

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

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

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

编辑:

Brownstone的注释不正确,这将按预期运行,如在Firebug控制台中运行所显示的那样

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

请注意,我与其他人一样,应该避免将字符串传递给它,setTimeout因为这将调用eval()字符串,而传递函数。


这将不起作用,因为postinsql(topicId)的结果将由setTimeout执行。您需要像第一个答案一样将其包装在函数中,或者使用诸如Prototype的.curry()-setTimeout(postinsql.curry(topidId),4000)之类的助手。
shuckster

2
@brownstone:不正确。超时触发时将评估该字符串。
英里

9

您可以通过以下方式将参数传递给setTimeout回调函数:

setTimeout(函数,毫秒,param1,param2,...)

例如。

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

function alertMsg(message) {
    alert(message)
}

1
谁能告诉我为什么这个答案不是首选?我认为这是最简单的方法!就像setTimeout( (p) => { console.log(p); }, 1000, "hi" );
Mazhar Zandsalimi

1
是的 这应该是公认的答案。从MDN:developer.mozilla.org/en-US/docs/Web/API/...


5

自问这个问题以来,我知道已经有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);
}

4

我知道它已经很老了,但我想在此添加我的(首选)口味。

我认为实现此目的的一种非常易读的方法是将传递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);

4

David Meister的答案似乎是处理可能在调用setTimeout()之后但在调用匿名函数之前立即更改的参数。但这太麻烦了,不是很明显。我发现了一种优雅的方法,可以使用IIFE(即称的函数表达式)完成几乎相同的事情。

在下面的示例中,将currentList变量传递给IIFE,该变量将其保存在其闭包中,直到调用延迟函数为止。即使变量currentList在显示的代码后立即更改,setInterval()也将做正确的事。

如果没有这种IIFE技术,setTimeout()则肯定会为h2DOM中的每个元素调用该函数,但是所有这些调用将仅看到最后一个 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>

4

通常,如果需要将函数作为带有特定参数的回调传递,则可以使用高阶函数。ES6非常优雅:

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

setTimeout(someFunction(params), 1000);

或如果someFunction是第一订单:

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

这是非常不兼容的
user151496

确实的优雅!谢谢
Velojet

3

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

听说您可以将topicId作为第三个参数传递给setTimeout函数。没有给出太多细节,但是我获得了足够的信息来使它起作用,并且在Safari中很成功。我不知道它们对“毫秒错误”的含义。在这里查看:

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


3

我如何解决这个阶段?

就像这样:

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


与其他答案相比,这过于复杂。
Dan Dascalescu 2015年

3

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

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

3

如果您想将变量作为参数传递,请尝试以下操作

如果要求是功能和var作为帕尔马然后尝试这个

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

如果需求仅是变量作为参数,则尝试此

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

您可以在ES5和ES6上尝试


2

您可以尝试使用“ apply()”的默认功能,例如,您可以根据需要在数组中传递更多数量的参数。

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

1

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://docs.microsoft.com/zh-cn/previous-versions/windows/internet-explorer/ie-developer/platform-apis/aa741500(v%3Dvs.85)


0

@Jiri Vetyska感谢您的帖子,但是您的示例中有问题。我需要将悬停的目标传递给超时的函数,然后尝试了您的方法。在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
}

希望这对其他人有用。


0

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

我们需要确保没有其他改变这些变量,并确保编写适当的递归条件以避免无限递归。


0

//这是三个非常简单明了的答案:

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

0

回答问题,但使用带有2个参数的简单加法函数。

var x = 3, y = 4;

setTimeout(function(arg1, arg2) { 
      delayedSum(arg1, arg2);
}(x, y), 1000);

function delayedSum(param1, param2) {
     alert(param1 + param2); // 7
}


-1

我想你要:

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

1
我遇到了一些实例,其中根本不起作用(总是导致“未定义函数”错误),但是使用匿名函数确实起作用。鉴于每个人似乎都在说上述语法应该始终有效,这令人沮丧。(可能是jQuery以某种方式妨碍了“ quote as string”方法吗?)
DA。

6
让我们假设topicId是一个函数...还是一个对象。这行不通!
瑟拉芬2011年

如果您确实要继续使用此方法,请JSON.stringify对常规对象和数组使用,然后JSON.parse在函数内部使用。但是,如果对象具有方法,则所有行为都会丢失。
DarthCadeus

-2

//这是三个非常简单明了的答案:

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');
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.