javascript 为什么多次 setTimeout() 调用会导致如此多的延迟?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4211046/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why do multiple setTimeout() calls cause so much lag?
提问by Niet the Dark Absol
I have a complex animation sequence involving fades and transitions in JavaScript. During this sequence, which consists of four elements changing at once, a setTimeoutis used on each element.
我有一个复杂的动画序列,涉及 JavaScript 中的淡入淡出和过渡。在这个由四个元素同时变化的序列中,setTimeout每个元素都使用了a 。
Tested in Internet Explorer 9, the animation works at realtime speed (it should take 1.6 seconds and it took exactly 1.6 seconds). ANY other browser will lag horribly, with animation times of 4 seconds (Firefox 3 and 4, Chrome, Opera) and something like 20 seconds in IE 8 and below.
在 Internet Explorer 9 中测试,该动画以实时速度运行(应该需要 1.6 秒,而正好需要 1.6 秒)。任何其他浏览器都会严重滞后,动画时间为 4 秒(Firefox 3 和 4、Chrome、Opera),而在 IE 8 及更低版本中则为 20 秒。
How can IE9 go so fast while all other browsers are stuck in the mud?
IE9 怎么能跑得这么快,而所有其他浏览器都陷入困境?
I have tried to find ways of merging the elements into one, so as to one have one setTimeout at any given time, but unfortunately it wouldn't stand up to any interference (such as clicking a different link to start a new animation before the current one has finished).
我试图找到将元素合并为一个的方法,以便在任何给定时间都有一个 setTimeout,但不幸的是它经不起任何干扰(例如单击不同的链接以在当前已完成)。
EDIT: To elaborate in response to comments, here's the outline of the code:
编辑:为了详细说明评论,这里是代码的大纲:
link.onclick = function() {
clearTimeout(colourFadeTimeout);
colourFadeTimeout = setTimeout("colourFade(0);",25);
clearTimeout(arrowScrollTimeout);
arrowScrollTimeout = setTimeout("arrowScroll(0);",25);
clearTimeout(pageFadeOutTimeout);
pageFadeOutTimeout = setTimeout("pageFadeOut(0);",25);
clearTimeout(pageFadeInTimeout);
pageFadeInTimeout = setTimeout("pageFadeIn(0);",25);
}
Each of the four functions progress the fade by one frame, then set another timeout with the argument incremented, until the end of the animation.
四个函数中的每一个都将淡入淡出一帧,然后设置另一个超时并增加参数,直到动画结束。
You can see the page at http://adamhaskell.net/cw/index.html(Username: knockknock; Password: goaway) (it has sound and music, which can be disabled, but be warned!) - my JavaScript is very messy since I haven't really organised it properly, but it is commented a bit so hopefully you can see what the general idea is.
你可以在http://adamhaskell.net/cw/index.html看到页面(用户名:knockknock;密码:goaway)(它有声音和音乐,可以禁用,但要注意!) - 我的 JavaScript 非常乱七八糟,因为我没有真正正确地组织它,但它被评论了一点,所以希望你能看到一般的想法是什么。
回答by slebetman
Several things:
几件事:
Your timeout is 25ms. This translates to 40fps which is a very high framerate to try to achieve via javascript. Especially for things involving DOM manipulation that may trigger reflows. Increase it to 50 or 60. 15fps should be more than fluid enough for the kinds of animation you're doing. You're not trying to display videos here, just move things around the page.
Don't use strings as the first parameter to
setTimeout(). Especially if you care about performance. That will force javascript to recompile the string each frame of animation. Use a function instead. If you need to pass an argument use an anonymous function to wrap the function you want to execute:setTimeout(function(){ pageFadeIn(0) },50);this will only get compiled once when the script is loaded.
As mentioned by Ben, it is cheaper to use a single setTimeout to schedule the functions. For that matter, code clarity may improve by using setInterval instead (or it may not, depends on your coding style).
您的超时为 25 毫秒。这转化为 40fps,这是尝试通过 javascript 实现的非常高的帧率。特别是对于涉及可能触发回流的 DOM 操作的事情。将其增加到 50 或 60。对于您正在执行的动画类型,15fps 应该足够流畅。您不是要在此处显示视频,只需在页面上移动内容即可。
不要使用字符串作为第一个参数
setTimeout()。特别是如果你关心性能。这将强制 javascript 重新编译每个动画帧的字符串。改用函数。如果需要传递参数,请使用匿名函数来包装要执行的函数:setTimeout(function(){ pageFadeIn(0) },50);这只会在加载脚本时编译一次。
正如 Ben 所提到的,使用单个 setTimeout 来调度函数会更便宜。就此而言,通过使用 setInterval 可能会提高代码清晰度(也可能不会,取决于您的编码风格)。
Additional answer:
补充回答:
Programming javascript animation is all about optimisation and compromise. It's possible to animate lots of things on the page with little slow-down but you need to know how to do it right and decide what to sacrifice. As an example of just how much can be animated at once is a demo real-time strategy game I wrote a couple of years ago.
编写 javascript 动画就是关于优化和妥协。可以在几乎没有减速的情况下对页面上的很多内容进行动画处理,但您需要知道如何正确执行并决定要牺牲什么。我几年前编写的一个演示实时战略游戏,可以说明一次可以制作多少动画。
Among the things I did to optimize the game are:
我为优化游戏所做的事情包括:
The walking soldiers are made up of only two frames of animation and I simply toggle between the two images. But the effect is very convincing nonetheless. You don't need perfect animation, just one that looks convincing.
I use a single setInterval for everything. It's cheaper CPU-wise and easier to manage. Just decide on a base frame rate and then schedule for different animation to start at different times.
行走的士兵仅由两帧动画组成,我只是在两个图像之间切换。但是效果还是非常有说服力的。你不需要完美的动画,只需要一个看起来令人信服的动画。
我对所有事情都使用一个 setInterval 。它在 CPU 方面更便宜,更易于管理。只需确定基本帧速率,然后安排不同的动画在不同的时间开始。
回答by Ben
Well, that's a lot of javascript (despite the "quadruple-dose of awesomeness" :)
嗯,这是很多 javascript(尽管有“四倍的惊人”:)
You're firing a lot of setTimeout sequence, I'm not sure how well JS engines are optimised for this.. particularly IE <= 8
你触发了很多 setTimeout 序列,我不确定 JS 引擎为此优化得有多好..特别是 IE <= 8
Ok, maybe just a rough suggestion... You could maybe write a small timing engine.
好吧,也许只是一个粗略的建议...你也许可以编写一个小的计时引擎。
Maintain a global object that stores your current running timed events with the function to run, and the delay...
维护一个全局对象,用于存储您当前正在运行的定时事件以及要运行的函数以及延迟...
Then have a single setTimeout handler that check against that global object, and decreases the delay at each iteration and call the function when the delay becomes < 0
然后有一个 setTimeout 处理程序来检查该全局对象,并减少每次迭代的延迟并在延迟变为 < 0 时调用该函数
you global event would looks something like that:
你的全球事件看起来像这样:
var events = {
fade1 : {
fn : func_name,
delay : 25,
params : {}
}
fadeArrow : {
fn : func_name,
delay : 500,
params : {}
}
slideArrow : {
fn : func_name,
delay : 500,
params : {
arrow:some_value
}
}
}
then create a function to loop through these at an interval (maybe 10 or 20 ms) and decrease your delays until they complete and fire the function with params as a paramer to the function (check Function.call for that).
然后创建一个函数以间隔(可能是 10 或 20 毫秒)循环遍历这些并减少延迟,直到它们完成并使用 params 作为函数的参数触发该函数(检查 Function.call 以了解该函数)。
Once down, fire setTimeout again with the same delay..
一旦关闭,以相同的延迟再次触发 setTimeout ..
To cancel an event just unset the property from the events list..
要取消事件,只需从事件列表中取消设置该属性即可。
Build a few method to add / remove queued events, update params and so on..
构建一些方法来添加/删除排队事件,更新参数等..
That would reduce everything to just one timeout handler..
这会将所有内容减少到只有一个超时处理程序..
(just an idea)
(只是一个想法)

