简体   繁体   English

为什么多个setTimeout()调用导致如此多的延迟?

[英]Why do multiple setTimeout() calls cause so much lag?

I have a complex animation sequence involving fades and transitions in JavaScript. 我有一个复杂的动画序列,涉及JavaScript中的淡入淡出和过渡。 During this sequence, which consists of four elements changing at once, a setTimeout is used on each element. 在此序列中,由四个元素一次更改组成,在每个元素上使用setTimeout

Tested in Internet Explorer 9, the animation works at realtime speed (it should take 1.6 seconds and it took exactly 1.6 seconds). 在Internet Explorer 9中测试,动画以实时速度运行(它应该需要1.6秒,并且需要1.6秒)。 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. 任何其他浏览器都会出现可怕的延迟,动画时间为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非常凌乱,因为我没有真正组织它,但它有点评论,所以希望你可以看到一般的想法是什么。

Several things: 几件事:

  1. Your timeout is 25ms. 你的超时是25ms。 This translates to 40fps which is a very high framerate to try to achieve via javascript. 这转换为40fps,这是一个非常高的帧速率,试图通过JavaScript实现。 Especially for things involving DOM manipulation that may trigger reflows. 特别是对于涉及DOM操作的事物,可能会引发回流。 Increase it to 50 or 60. 15fps should be more than fluid enough for the kinds of animation you're doing. 将它增加到50或60.对于你正在做的各种动画,15fps应该足够流畅。 You're not trying to display videos here, just move things around the page. 你不是试图在这里显示视频,只是在页面上移动东西。

  2. Don't use strings as the first parameter to setTimeout() . 不要使用字符串作为setTimeout()的第一个参数。 Especially if you care about performance. 特别是如果你关心表现。 That will force javascript to recompile the string each frame of animation. 这将强制javascript重新编译每帧动画的字符串。 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. 这只会在加载脚本时编译一次。

  3. As mentioned by Ben, it is cheaper to use a single setTimeout to schedule the functions. 正如Ben所提到的,使用单个setTimeout来调度函数会更便宜。 For that matter, code clarity may improve by using setInterval instead (or it may not, depends on your coding style). 就此而言,代码清晰度可以通过使用setInterval来改进(或者它可能不会,取决于您的编码风格)。


Additional answer: 补充答案:

Programming javascript animation is all about optimisation and compromise. 编程javascript动画是关于优化和妥协的。 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. 作为一个可以立刻动画多少的一个例子是我几年前写的一个演示即时战略游戏。

Among the things I did to optimize the game are: 我为优化游戏所做的事情包括:

  1. 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. 你不需要完美的动画,只需要一个看起来令人信服的动画。

  2. I use a single setInterval for everything. 我使用一个setInterval来处理所有事情。 It's cheaper CPU-wise and easier to manage. 它的CPU更便宜,更易于管理。 Just decide on a base frame rate and then schedule for different animation to start at different times. 只需确定基本帧速率,然后安排不同的动画在不同的时间开始。

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作为函数的paramer来激活函数(检查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.. 构建一些方法来添加/删除排队事件,更新params等等。

That would reduce everything to just one timeout handler.. 这会将所有内容减少到一个超时处理程序..

(just an idea) (只是一个想法)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM