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

IT技术 javascript parameters callback settimeout
2021-02-01 00:55:17

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

6个回答
setTimeout(function() {
    postinsql(topicId);
}, 4000)

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

更新:

正如 Hobblin 在他对该问题的评论中所说,现在您可以使用Function.prototype.bind().

例子:

setTimeout(postinsql.bind(null, topicId), 4000);
如果在设置超时之后但在调用函数之前更改 topicId 会发生什么?
2021-03-11 00:55:17
window.setTimeout是一个 DOM 方法,因此 ECMAScript 规范没有定义。传递字符串在浏览器中一直有效,并且是事实上的标准——事实上,传递函数对象的能力是后来添加的,使用 JavaScript 1.2——它明确地成为 HTML5 草案规范的一部分 ( whatwg.org/specs/web -apps/current-work/multipage/…)。然而,使用字符串而不是函数对象通常被认为是糟糕的风格,因为它本质上是一种延迟的形式eval()
2021-03-14 00:55:17
@pilau 使用另一个闭包将有助于 topicId=12; 函数 postinsql(topicId){ console.log(topicId); } function setTimeOutWithClosure(topicId){ setTimeout(function() { postinsql(topicId); }, 1000) } setTimeOutFunction(topicId); topicId=13;
2021-03-18 00:55:17
@pilau 这正是我的问题:如果匿名函数中使用的变量在超时之前发生变化(例如在 for 循环中),那么它也会在函数内部发生变化。因此,在我的示例中,在 for 循环中设置 5 个不同的超时实际上最终使用了相同的变量。使用此答案时要小心!
2021-03-24 00:55:17
var temp = setTimeout(function() { postinsql(topicId); }, 4000); 清除超时(温度);??
2021-04-01 00:55:17

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

例子:

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

更多细节:

根据developer.mozilla.org/es/docs/Web/API/WindowTimers/setTimeout 仅在版本 >=10 中支持 Internet Explorer 的回调参数,请小心,因为在许多站点 ie8 和 ie9 仍然获得一些相关份额。
2021-03-12 00:55:17
我不确定为什么这个答案没有被选为最佳答案。使用匿名函数当然可以,但是如果您可以简单地将第三个参数传递给原始的 setTimeout 函数调用......为什么不呢?
2021-03-17 00:55:17
更好的答案。如果您有代码在“setTimeout”调用和匿名函数的实际执行之间修改您的参数 - 匿名函数将收到修改后的值,而不是在 setTimeout 调用时的值。例如:for(var i = 0; i < 100; i++) { setTimeout(function() { console.write(i); }, 0); 这将记录 "100" 100 次(在 FF 上测试)。当前的答案有助于避免这种情况。
2021-03-21 00:55:17
这个答案实际上使我能够传递一个事件对象,而其他方法则没有。我已经有了一个匿名函数。
2021-03-27 00:55:17
因为它在仍然非常流行的 IE 版本中不起作用。
2021-04-08 00:55:17

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

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

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

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

我只是用 var that = this; setTimeout( function() { that.foo(); }, 1000);
2021-03-10 00:55:17
这是正确的,它是在 HTML5 中指定的。 whatwg.org/specs/web-apps/current-work/multipage/...
2021-03-12 00:55:17
我必须悲痛地通知:这在 Internet Explorer 中不起作用。:/ 所有额外的参数都是未定义的。
2021-03-17 00:55:17
根据developer.mozilla.org/es/docs/Web/API/WindowTimers/setTimeout 仅在版本 >=10 中支持 Internet Explorer 的回调参数,请小心,因为在许多站点 ie8 和 ie9 仍然获得一些相关份额。
2021-03-22 00:55:17
这与Fabio 的答案完全相同
2021-03-28 00:55:17

这是一个非常古老的问题,已经有了“正确”的答案,但我想我会提到另一种这里没有人提到的方法。这是从优秀的下划线复制粘贴的

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

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

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

@Melanie“一些图书馆”?我在答案中说它是下划线库 - underscorejs.org但是,是的,Array.prototype.slice 在该库中被别名为切片,所以如果你不使用它,你必须自己做,好地方 :)
2021-03-11 00:55:17
这个答案确实有效,但你似乎有一些我没有的图书馆。这是它工作的小修复:而不是 slice.call,使用 Array.prototype.slice.call(arguments, 2)
2021-04-01 00:55:17

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

方法一

根据 Sukima 的建议使用forEachand 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/

方法三

或者,如果您不能使用forEachor bind,请使用IIFE

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

方法四

但如果你不关心 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

注意:.bind不适用于 IE8 及以下 [参考:developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/... ]。我最终使用了 Schien 的解决方案:stackoverflow.com/a/21213723/1876899
2021-03-25 00:55:17
如果您在使用的环境中,bind那么您也在提供Object.keysforEach. 您可以松开 for 循环并在此过程中获得“免费”(如两只鸟,一块石头免费而不是资源免费)功能范围。
2021-03-27 00:55:17
@David Sherret 如果您以前没有使用过它,请务必查看async库(github.com/caolan/async)。我们在 Sails 中广泛使用它,并且在过去 2 年中取得了很好的效果。它提供了在平行和在异步系列方法forEachmapreduce等。
2021-03-31 00:55:17