使用 setTimeout() 调用函数
简单地说...
为什么
setTimeout('playNote('+currentaudio.id+', '+noteTime+')', delay);
工作正常,在指定的延迟后调用该函数,但
setTimeout(playNote(currentaudio.id,noteTime), delay);
同时调用函数 playNote ?
(这些 setTimeout() 在 for 循环中)
或者,如果我的解释太难读,这两个函数之间有什么区别?
Simply put...
why does
setTimeout('playNote('+currentaudio.id+', '+noteTime+')', delay);
work perfectly, calling the function after the the specified delay, but
setTimeout(playNote(currentaudio.id,noteTime), delay);
calls the function playNote all at the same time?
(these setTimeout()s are in a for loop)
or, if my explanation is too hard to read, what is the difference between the two functions?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
您列出的第一个形式有效,因为它将在
delay
末尾计算一个字符串。使用eval()
通常不是一个好主意,因此您应该避免这种情况。第二种方法不起作用,因为您立即使用 执行函数对象函数调用运算符
()
。最终发生的情况是,如果您使用playNote(...)
形式,则playNote
会立即执行,因此在延迟结束时不会发生任何事情。相反,您必须将匿名函数传递给 setTimeout,因此正确的形式是:
请注意,您正在传递
setTimeout
整个函数表达式,因此它将保留匿名函数并仅在以下位置执行它:延迟结束。您还可以传递
setTimeout
引用,因为引用不会立即执行,但您无法传递参数:注意:
对于重复事件,您可以使用
setInterval()
和你可以将setInterval()
设置为一个变量,并使用该变量来停止间隔clearInterval()
。您说您在
for
循环中使用setTimeout()
。在许多情况下,最好在递归函数中使用setTimeout()
。这是因为在for
循环中,setTimeout()
中使用的变量将不再是setTimeout()
开始时的变量,但变量在函数被触发时的延迟之后。只需使用递归函数来回避整个问题。
使用递归来处理可变延迟时间:
The first form that you list works, since it will evaluate a string at the end of
delay
. Usingeval()
is generally not a good idea, so you should avoid this.The second method doesn't work, since you immediately execute a function object with the function call operator
()
. What ends up happening is thatplayNote
is executed immediately if you use the formplayNote(...)
, so nothing will happen at the end of the delay.Instead, you have to pass an anonymous function to setTimeout, so the correct form is:
Note that you are passing
setTimeout
an entire function expression, so it will hold on to the anonymous function and only execute it at the end of the delay.You can also pass
setTimeout
a reference, since a reference isn't executed immediately, but then you can't pass arguments:Note:
For repeated events you can use
setInterval()
and you can setsetInterval()
to a variable and use the variable to stop the interval withclearInterval()
.You say you use
setTimeout()
in afor
loop. In many situations, it is better to usesetTimeout()
in a recursive function. This is because in afor
loop, the variables used in thesetTimeout()
will not be the variables as they were whensetTimeout()
began, but the variables as they are after the delay when the function is fired.Just use a recursive function to sidestep this entire problem.
Using recursion to deal with variable delay times:
试试这个。
Try this.
不要使用字符串超时。它是有效的
eval
,但这是一件坏事。它之所以有效,是因为它将currentaudio.id
和noteTime
转换为它们自身的字符串表示形式并将其隐藏在代码中。仅当这些值具有生成 JavaScript 文字语法并重新创建该值的toString()
时,这种情况才有效,对于Number
来说是这样,但对于其他情况则不然。这是一个函数调用。
playNote
立即被调用,函数的返回结果(可能是undefined
)被传递给setTimeout()
,而不是您想要的。正如其他答案提到的,您可以使用带有闭包的内联函数表达式来引用
currentaudio
和noteTime
:但是,如果您处于循环中并且
currentaudio< /code> 或
noteTime
每次循环时都不同,您会遇到闭环循环问题:每次超时都会引用相同的变量,因此当调用它们时,您将得到每次都使用相同的值,即循环早些时候完成时变量中保留的值。您可以使用另一个闭包来解决这个问题,为循环的每次迭代获取变量值的副本:
但这现在变得有点难看。更好的是
Function#bind
,它会为您部分应用一个函数:(window
用于设置函数内this
的值,这是bind()
的一个功能,您在这里不需要。)但是,这是 ECMAScript 第五版的功能,并非所有浏览器都支持。因此,如果你想使用它,你必须首先获得支持,例如:
Don't use string-timeouts. It's effective an
eval
, which is a Bad Thing. It works because it's convertingcurrentaudio.id
andnoteTime
to the string representations of themselves and hiding it in the code. This only works as long as those values havetoString()
s that generate JavaScript literal syntax that will recreate the value, which is true forNumber
but not for much else.that's a function call.
playNote
is called immediately and the returned result of the function (probablyundefined
) is passed tosetTimeout()
, not what you want.As other answers mention, you can use an inline function expression with a closure to reference
currentaudio
andnoteTime
:However, if you're in a loop and
currentaudio
ornoteTime
is different each time around the loop, you've got the Closure Loop Problem: the same variable will be referenced in every timeout, so when they're called you'll get the same value each time, the value that was left in the variable when the loop finished earlier.You can work around this with another closure, taking a copy of the variable's value for each iteration of the loop:
but this is getting a bit ugly now. Better is
Function#bind
, which will partially-apply a function for you:(
window
is for setting the value ofthis
inside the function, which is a feature ofbind()
you don't need here.)However this is an ECMAScript Fifth Edition feature which not all browsers support yet. So if you want to use it you have to first hack in support, eg.:
我在这个网站上创建了一个帐户来评论 Peter Ajtai 的答案(目前投票最高),却发现你需要 50 名代表(无论是什么)来发表评论,所以我将其作为答案,因为它可能值得指出出了几件事。
在他的回答中,他陈述如下:
这是不正确的。为 setTimeout 提供函数引用和延迟量后,任何其他参数都会被解析为引用函数的参数。下面的代码比将函数调用包装在函数中更好。
请始终查阅文档。
也就是说,正如 Peter 指出的那样,如果您想改变每个
playNote()
之间的延迟,那么递归函数将是一个好主意,或者考虑使用setInterval()
如果您希望每个playNote()
之间有相同的延迟。另外值得注意的是,如果您想将 for 循环的
i
解析为setTimeout()
,则需要将其包装在一个函数中,详细信息 此处。I literally created an account on this site to comment on Peter Ajtai's answer (currently highest voted), only to discover that you require 50 rep (whatever that is) to comment, so I'll do it as an answer since it's probably worth pointing out a couple things.
In his answer, he states the following:
This isn't true. After giving
setTimeout
a function reference and delay amount, any additional arguments are parsed as arguments for the referenced function. The below would be better than wrapping a function call in a function.Always consult the docs.
That said, as Peter points out, a recursive function would be a good idea if you want to vary the delay between each
playNote()
, or consider usingsetInterval()
if you want there to be the same delay between eachplayNote()
.Also worth noting that if you want to parse the
i
of your for loop into asetTimeout()
, you need to wrap it in a function, as detailed here.它可能有助于理解 javascript 何时执行代码,以及何时等待执行某些内容:
let foo2 = function foo(bar=baz()){ console.log(bar); return bar()}
=>
语法,并且您会得到类似的结果(但不相同)结果。foo
或foo2
传递给setTimeout
, 然后在超时后,它将调用该函数,就像您执行foo()
一样(请注意,没有参数传递给foo
。这是因为foo()
)。 >setTimeout 默认情况下不会传递参数,尽管可以,但这些参数会在超时到期之前计算,而不是在超时到期时计算。)bar
的默认参数时,首先 javascript 会查找名为baz
的变量。如果找到,它就会尝试将其作为函数调用。如果有效,它将返回值保存到bar
。bar
,然后使用结果调用 console.log。这不叫吧。但是,如果它被调用为bar()
,那么bar
将首先运行,然后bar()
的返回值将是而是传递给console.log
。请注意,javascript 在调用该函数之前,甚至在查找该函数以查看它是否存在并且确实是一个函数之前,都会获取它所调用的函数的参数值。bar
,然后尝试将其作为函数调用。如果有效,该值将作为 foo() 的结果返回。因此,函数体和默认参数不会立即调用,但其他所有内容都会立即调用。类似地,如果您执行函数调用(即
()
),那么该函数也会立即执行。但是,您不需要调用函数。省略括号将允许您传递该函数并稍后调用它。但这样做的缺点是您无法指定调用函数时使用的参数。此外,JavaScript 在调用函数或查找存储函数的变量之前,会执行函数括号内的所有操作。It may help to understand when javascript executes code, and when it waits to execute something:
let foo2 = function foo(bar=baz()){ console.log(bar); return bar()}
=>
syntax, and you get similar (but not identical) results.foo2
baz
norbar
, no values looked up, etc. However, the syntax has been checked inside the function.foo
orfoo2
tosetTimeout
then after the timeout, it would call the function, the same as if you didfoo()
. (notice that no args are passed tofoo
. This is becausesetTimeout
doesn't by default pass arguments, although it can, but those arguments get evaluated before the timeout expires, not when it expires.)bar
is evaluated. (This would not have happened if we passed an argument)bar
, first javascript looks for a variable namedbaz
. If it finds one, it then tries to call it as a function. If that works, it saves the return value tobar
.bar
and then calls console.log with the result. This does not call bar. However, if it was instead called asbar()
, thenbar
would run first, and then the return value ofbar()
would be passed toconsole.log
instead. Notice that javascript gets the values of the arguments to a function it is calling before it calls the function, and even before it looks up the function to see if it exists and is indeed a function.bar
, and then it tries to call it as a function. If that works, the value is returned as the result offoo()
So, function bodies and default arguments are not called immediately, but everything else is. Similarly, if you do a function call (i.e.
()
), then that function is executed immediately as well. However, you aren't required to call a function. Leaving off the parentheses will allow you to pass that function around and call it later. The downside of that, though, is that you can't specify the arguments you want the function to be called with. Also, javascript does everything inside the function parentheses before it calls the function or looks up the variable the function is stored in.因为第二个你告诉它首先调用 playNote 函数,然后将它的返回值传递给 setTimeout。
Because the second one you're telling it to call the playNote function first and then pass the return value from it to setTimeout.