关于javascript:如何将参数传递给setTimeout()回调?

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

我得到一个错误,没有定义topicId。在我使用setTimeout()功能之前,一切都正常。

我希望在一段时间后调用我的postinsql(topicId)函数。我该怎么办?


1
2
3
setTimeout(function() {
    postinsql(topicId);
}, 4000)

您需要将一个匿名函数作为参数而不是字符串,后者的方法甚至不应该按照ECMAScript规范工作,但是浏览器只是很宽容。这是正确的解决方案,在使用setTimeout()setInterval()时,不要依赖于将字符串作为"函数"传递,因为必须对其进行评估,而这是不正确的,所以速度较慢。

更新:

正如霍布林在他对这个问题的评论中所说,现在您可以使用Function.prototype.bind()将参数传递给settimeout内的函数。

例子:

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()和超时之间的某个时间点改变值,那么……这已经不再令人沮丧了。)

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


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

方法1

根据Sukima的建议,使用forEachObject.keys

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

使用bind

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

或者如果您不能使用forEachbind,请使用iife:

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中使用Object.keysforEach


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

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

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 in topicId, so the registered function with a delay has access to topicId 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
})();

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


最简单的跨浏览器解决方案,支持设置超时中的参数:

1
2
3
setTimeout(function() {
    postinsql(topicId);
}, 4000)

如果您不介意不支持IE 9及更低版本:

1
setTimeout(postinsql, 4000, topicId);

setTimeout desktop browser compatibility

setTimeout mobile browser compatibility

https://developer.mozilla.org/en-us/docs/web/api/windowtimers/settimeout


我知道它很老,但我想加上我喜欢的味道。

我认为实现这一点的一个非常易读的方法是将topicId传递给一个函数,该函数反过来使用参数在内部引用主题ID。即使不久后外部的topicId会改变,这个值也不会改变。

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(立即被邀请的函数表达式)做几乎相同事情的优雅方法。

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

如果没有这种IIFE技术,将绝对为DOM中的每个h2元素调用setTimeout()函数,但所有这些调用将只看到最后一个h2元素的文本值。

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

或者,如果someFunction是一阶:

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