我有一些 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)
您需要将匿名函数作为参数而不是字符串提供,后一种方法甚至不应该根据 ECMAScript 规范工作,但浏览器只是宽松的。这是正确的解决方案,在使用 setTimeout()
或 setInterval()
时,永远不要依赖将字符串作为“函数”传递,它会比较慢,因为它必须被评估而且它是不正确的。
更新:
正如 Hobblin 在他的 comments 中对问题所说,现在您可以使用 Function.prototype.bind()
将参数传递给 setTimeout 内的函数。
例子:
setTimeout(postinsql.bind(null, topicId), 4000);
在现代浏览器(即 IE11 及更高版本)中,“setTimeout”接收第三个参数,该参数在计时器结束时作为参数发送给内部函数。
例子:
var hello = "你好世界";设置超时(警报,1000,你好);
更多细节:
https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers.setTimeout
http://arguments.callee.info/2008/11/10/passing-arguments-to-settimeout-and-setinterval/
经过一些研究和测试,唯一正确的实现是:
setTimeout(yourFunctionReference, 4000, param1, param2, paramN);
setTimeout 会将所有额外参数传递给您的函数,以便在那里处理它们。
匿名函数可以用于非常基本的东西,但是在你必须使用“this”的对象实例中,没有办法让它工作。任何匿名函数都会将“this”更改为指向窗口,因此您将丢失对象引用。
var that = this; setTimeout( function() { that.foo(); }, 1000);
这是一个非常古老的问题,答案已经“正确”,但我想我会提到另一种在这里没有人提到的方法。这是从优秀的 underscore 库中复制和粘贴的:
_.delay = function(func, wait) {
var args = slice.call(arguments, 2);
return setTimeout(function(){ return func.apply(null, args); }, wait);
};
您可以将任意数量的参数传递给 setTimeout 调用的函数,作为额外的奖励(通常是奖励),当您调用 setTimeout 时,传递给函数的参数的值会被冻结,所以如果它们改变值在调用 setTimeout() 和超时之间的某个时间点,嗯......这不再那么令人沮丧了:)
Here's a fiddle 你可以在哪里看到我的意思。
我最近遇到了需要在 循环 中使用 setTimeout
的独特情况。了解这一点可以帮助您了解如何将参数传递给 setTimeout
。
方法一
根据 Sukima 的 suggestion,使用 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/
方法三
或者,如果您不能使用 forEach
或 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 的 suggestion:
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.keys
和 forEach
。
.bind
不适用于 IE8 及以下 [ref: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… ]。我最终使用了 Schien 的解决方案:stackoverflow.com/a/21213723/1876899
bind
的环境中,那么您也在提供 Object.keys
和 forEach
的环境中。您可以松开 for 循环并在此过程中获得“免费”(如两只鸟,一只石头免费而不是资源免费)功能范围。
async
库 (github.com/caolan/async)。我们在 Sails 中广泛使用它,并且在过去 2 年中取得了很好的成果。它为异步 forEach
、map
、reduce
等提供并行和串行方法。
霍布林已经对这个问题发表了评论,但它真的应该是一个答案!
使用 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
setTimeout(postinsql.bind(this, topicId), 4000);
的第一个参数一起传递
bind
掩盖了部分应用程序。它确实产生了一些可读的代码。
您可以将参数传递给 setTimeout 回调函数,如下所示:
setTimeout(函数, 毫秒, param1, param2, ...)
例如。
function myFunction() {
setTimeout(alertMsg, 3000, "Hello");
}
function alertMsg(message) {
alert(message)
}
setTimeout( (p) => { console.log(p); }, 1000, "hi" );
一样简单
有些答案是正确的,但令人费解。
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 对象可能不一样了。因此,pre-bind the variables 很重要。
我知道这个问题被问到已经 10 年了,但是,如果你已经滚动到这里,我认为你仍然面临一些问题。 Meder Omuraliev 的解决方案是最简单的解决方案,可能对我们大多数人有所帮助,但对于那些不想有任何约束力的人来说,这里是:
使用参数设置超时
setTimeout(function(p){
//p == param1
},3000,param1);
使用立即调用函数表达式 (IIFE)
let param1 = 'demon';
setTimeout(function(p){
// p == 'demon'
},2000,(function(){
return param1;
})()
);
问题的解决方案
function statechangedPostQuestion()
{
//alert("statechangedPostQuestion");
if (xmlhttp.readyState==4)
{
setTimeout(postinsql,4000,(function(){
return xmlhttp.responseText;
})());
}
}
function postinsql(topicId)
{
//alert(topicId);
}
代替
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()
,而是传递一个函数。
我的答案:
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);
编辑:我看到了同样的答案,所以看看他的。但我没有偷他的答案!我只是忘了看。阅读解释,看看它是否有助于理解代码。
topicId
这样的值被改变时,超时值也会改变。克隆修复了它
支持setTimeout中参数的最简单的跨浏览器解决方案:
setTimeout(function() {
postinsql(topicId);
}, 4000)
如果您不介意不支持 IE 9 及更低版本:
setTimeout(postinsql, 4000, topicId);
https://i.stack.imgur.com/GhPoR.png
https://i.stack.imgur.com/VL5gE.png
https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout
我知道它很旧,但我想在其中添加我的(首选)风味。
我认为实现此目的的一种非常易读的方法是将 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);
David Meister 的答案似乎处理了在调用 setTimeout() 之后但在调用匿名函数之前可能立即更改的参数。但是太麻烦了,也不是很明显。我发现了一种使用 IIFE(立即调用函数表达式)做几乎相同事情的优雅方式。
在下面的示例中,currentList
变量被传递给 IIFE,IIFE 将其保存在其闭包中,直到调用延迟函数。即使变量 currentList
在显示代码后立即更改,setInterval()
也会做正确的事情。
如果没有这种 IIFE 技术,肯定会为 DOM 中的每个 h2
元素调用 setTimeout()
函数,但所有这些调用只会看到 last 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>
一般来说,如果需要将函数作为带有特定参数的回调传递,则可以使用高阶函数。这在 ES6 中非常优雅:
const someFunction = (params) => () => {
//do whatever
};
setTimeout(someFunction(params), 1000);
或者如果 someFunction
是一阶:
setTimeout(() => someFunction(params), 1000);
请注意,根据错误消息,topicId 被“未定义”的原因是它在执行 setTimeout 时作为局部变量存在,但在对 postinsql 的延迟调用发生时不存在。变量生命周期特别需要注意,尤其是在尝试将“this”作为对象引用传递时。
听说可以将 topicId 作为第三个参数传递给 setTimeout 函数。没有给出太多细节,但我得到了足够的信息来让它工作,它在 Safari 中是成功的。不过,我不知道他们对“毫秒错误”的含义。在这里查看:
http://www.howtocreate.co.uk/tutorials/javascript/timers
我如何解决这个阶段?
就像这样:
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:对不起,英语不是我的母语!
这适用于所有浏览器(IE 很奇怪)
setTimeout( (function(x) {
return function() {
postinsql(x);
};
})(topicId) , 4000);
如果你想将变量作为参数传递让我们试试这个
如果要求是函数并且 var 作为 parmas,那么试试这个
setTimeout((param1,param2) => {
alert(param1 + param2);
postinsql(topicId);
},2000,'msg1', 'msg2')
如果要求只是作为参数的变量,那么试试这个
setTimeout((param1,param2) => { alert(param1 + param2) },2000,'msg1', 'msg2')
你可以用 ES5 和 ES6 试试这个
setTimeout 是 WHAT WG 定义的 DOM 的一部分。
https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html
你想要的方法是:—
handle = self.setTimeout( handler [, timeout [, arguments... ] ] ) 在 timeout 毫秒后安排超时以运行处理程序。任何参数都直接传递给处理程序。
setTimeout(postinsql, 4000, topicId);
显然,IE10 支持额外的参数。或者,您可以使用 setTimeout(postinsql.bind(null, topicId), 4000);
,但是传递额外的参数更简单,这是更可取的。
历史事实:在 VBScript 时代,在 JScript 中,setTimeout 的第三个参数是语言,作为字符串,默认为“JScript”,但可以选择使用“VBScript”。 https://docs.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/platform-apis/aa741500(v%3Dvs.85)
您可以尝试像这样的“apply()”的默认功能,您可以在数组中传递更多数量的参数作为您的要求
function postinsql(topicId)
{
//alert(topicId);
}
setTimeout(
postinsql.apply(window,["mytopic"])
,500);
@Jiri Vetyska 感谢您的帖子,但是您的示例中有问题。我需要将悬停的目标(this)传递给超时函数,我尝试了你的方法。在 IE9 中测试 - 不起作用。我还进行了一些研究,似乎正如 here 所指出的,第三个参数是正在使用的脚本语言。没有提及其他参数。
所以,我按照@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 中的第三个可选参数存在问题,并且使用闭包会阻止我们更改变量(例如在循环中)并仍然达到预期的结果,我建议采用以下解决方案。
我们可以尝试像这样使用递归:
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);
}
我们需要确保没有其他任何东西会改变这些变量,并且我们需要编写一个适当的递归条件来避免无限递归。
// 这是三个非常简单明了的答案:
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');
//一些函数,有一些参数,需要带参数运行 var a = function a(b, c, d, e){console.log(b, c, d, e);} //另一个函数,其中setTimeout 用于函数“a”,这具有相同的参数 var f = function f(b, c, d, e){ setTimeout(a.apply(this, arguments), 100);} f(1,2,3 ,4); //运行 //另一个函数,其中 setTimeout 用于函数“a”,但其他一些参数使用不同的顺序 var 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);
// 这是三个非常简单明了的答案:
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
函数调用中删除 quotes,如下所示:
setTimeout(postinsql(topicId),4000);
postinsql()
,而不是在 4000 毫秒的时间内。
我想你想要:
setTimeout("postinsql(" + topicId + ")", 4000);
JSON.stringify
用于常规对象和数组,然后在函数内使用 JSON.parse
。但是,如果对象具有方法,则所有行为都将丢失。
回答这个问题,但通过一个带有 2 个参数的简单加法函数。
var x = 3, y = 4;
setTimeout(function(arg1, arg2) {
delayedSum(arg1, arg2);
}(x, y), 1000);
function delayedSum(param1, param2) {
alert(param1 + param2); // 7
}
不定期副业成功案例分享
window.setTimeout
是一种 DOM 方法,因此不在 ECMAScript 规范中定义。传递字符串在浏览器中一直有效,并且是 de facto 标准——事实上,传递函数对象的能力是后来在 JavaScript 1.2 中添加的——它明确地是 HTML5 草案规范的一部分( whatwg.org/specs/web-apps/current-work/multipage/…)。然而,使用字符串而不是函数对象通常被认为是糟糕的风格,因为它本质上是延迟eval()
的一种形式。