Javascript:setTimeout和界面冻结 [英] Javascript : setTimeout and interface freezing

查看:98
本文介绍了Javascript:setTimeout和界面冻结的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有大约10个复杂的图表,每个图表需要5秒才能刷新。如果我在这10个图表上进行循环,则刷新大约需要50秒。在这50秒期间,用户可以移动滚动条。如果移动滚动条,则必须停止刷新,当滚动条停止移动时,再次进行刷新。

I've got about 10 complex graphs which take 5sec each to refresh. If I do a loop on these 10 graphs, it takes about 50 seconds to refresh. During these 50 seconds, the user can move a scrollbar. If the scrollbar is moved, the refresh must stop and when the scrollbar stops to move, the refresh occurs again.

我在循环中使用setTimeout函数让界面刷新。
算法是:

I'm using the setTimeout function inside the loop to let the interface refresh. the algorithm is :


  • 渲染第一张图

  • setTimeout(渲染第二个)图,200)

  • 当渲染第二个图形时,在200ms内渲染第三个图形,依此类推

setTimeout允许我们捕获滚动条事件并清除下次刷新的时间以避免在移动滚动条之前等待50秒...

The setTimeout allows us to catch the scrollbar event and to clearTimeout the next refresh to avoid to wait 50sec before moving the scrollbar...

问题是它不会随时运行。

The problem is that it does not run anytime.

采用以下简单代码(您可以在这个小提琴中尝试: http://jsfiddle.net/BwNca/5/

Take the simple following code (you can try it in this fiddle : http://jsfiddle.net/BwNca/5/) :

HTML:

<div id="test" style="width: 300px;height:300px; background-color: red;">

</div>
<input type="text" id="value" />
<input type="text" id="value2" />

Javascript:

Javascript :

var i = 0;
var j = 0;
var timeout;
var clicked = false;

// simulate the scrollbar update : each time mouse move is equivalent to a scrollbar move
document.getElementById("test").onmousemove = function() {

    // ignore first move (because onclick send a mousemove event)
    if (clicked) {
        clicked = false;
        return;
    }

    document.getElementById("value").value = i++; 
    clearTimeout(timeout);
}

// a click simulates the drawing of the graphs
document.getElementById("test").onclick = function() {
    // ignore multiple click
    if (clicked) return;

    complexAlgorithm(1000);    
    clicked = true;   
}

// simulate a complexe algorithm which takes some time to execute (the graph drawing)
function complexAlgorithm(milliseconds) {
  var start = new Date().getTime();
  for (var i = 0; i < 1e7; i++) {
    if ((new Date().getTime() - start) > milliseconds){
      break;
    }
  }   

  document.getElementById("value2").value = j++;

  // launch the next graph drawing
  timeout =  setTimeout(function() {complexAlgorithm(1000);}, 1);
}

代码:


  • 当你将鼠标移动到红色div时,它会更新一个计数器

  • 当你点击红色div时,它会模拟1秒的大处理(所以它冻结界面由于javascript单线程)

  • 冻结后,等待1ms,然后重新模拟处理,依此类推,直到鼠标再次移动

  • 当鼠标再次移动时,它会中断超时以避免无限循环。

  • when you move your mouse into the red div, it updates a counter
  • when you click on the red div, it simulates a big processing of 1sec (so it freezes the interface due to javascript mono thread)
  • after the freezing, wait 1ms, and resimulate the processing and so on until the mouse move again
  • when the mouse move again, it breaks the timeout to avoid infinite loop.

当您在冻结期间单击一次并移动鼠标时,我认为将在setTimeout发生时执行的下一个代码是mousemove事件的代码(因此它将取消超时和冻结)但是有时点击计数器会获得2点或更多点,而不是因为mouvemove事件而只获得1点......

When you click one time and move the mouse during the freeze, I was thinking that the next code that will be executed when a setTimeout will occurs is the code of the mousemove event (and so it will cancel the timeout and the freeze) BUT sometimes the counter of click gains 2 or more points instead of gaining only 1 point due to the mouvemove event...

此测试的结论:setTimeout函数并不总是释放在mousemove事件期间执行代码的资源,但有时会在执行另一个代码之前保留线程并在settimeout回调中执行代码。

Conclusion of this test : the setTimeout function does not always release resource to execute a code during a mousemove event but sometimes kept the thread and execute the code inside the settimeout callback before executing another code.

这对我们的影响是例如,用户可以等待10秒(渲染2个图形),而不是在使用滚动条之前等待5秒。这非常烦人,我们需要避免这种情况,并确保在渲染阶段移动滚动条时只渲染一个图形(以及其他取消)。

The impact of this is that in our real example, the user can wait 10 sec (2 graphs are rendered) instead of waiting 5 seconds before using the scrollbar. This is very annoying and we need to avoid this and to be sure that only one graph is rendered (and other canceled) when the scrollbar is moved during a render phase.

如何确保在鼠标移动时中断超时?

How to be sure to break the timeout when the mouse move ?

PS:在下面的简单示例中,如果用200ms更新超时,则所有运行都完美但是它是不是一个可接受的解决方案(真正的问题是更复杂,200ms计时器和复杂的界面会出现问题)。请不要提供优化图形渲染的解决方案,这不是问题所在。

PS: in the simple example below, if you update the timeout with 200ms, all runs perfectly but it is not an acceptable solution (the real problem is more complex and the problem occurs with a 200ms timer and a complex interface). Please do not provide a solution as "optimize the render of the graphs", this is not the problem here.

编辑:cernunnos对问题的解释更好:
此外,通过阻塞循环上的进程,您确保在该循环完成之前不会处理任何事件,因此任何事件都只会在每个循环执行之间处理(并且超时清除) (因此你有时在中断之前必须等待2次或更多次完全执行)

EDIT : cernunnos has a better explanation of the problem : Also, by "blocking" the process on your loop you are ensuring no event can be handled until that loop has finished, so any event will only be handled (and the timeout cleared) inbetween the execution of each loop (hence why you sometimes have to wait for 2 or more full executions before interrupting).

问题恰好包含在粗体字中:我想成为确保在我想要的时候中断执行,而不是在中断之前等待2次或更多次完全执行

The problem is exactly contains in bold words : I want to be sure to interrupt the execution when I want and not to wait 2 or more full executions before interrupting

总结:采用这个jsfiddle: http:// jsfiddle。 net / BwNca / 5 / (上面的代码)。

In summary : takes this jsfiddle : http://jsfiddle.net/BwNca/5/ (the code above).

更新此jsfiddle并提供解决方案:

Update this jsfiddle and provide a solution to :

鼠标继续前进红色的div。然后单击并继续移动:右侧计数器必须仅提升一次。但有时它会在第一个计数器再次运行之前提高2到3次......这就是问题,它必须只提升一次!

Mouse move on the red div. Then click and continue moving : the right counter must raise only once. But sometimes it raises 2 or 3 times before the first counter can run again... this is the problem, it must raise only once !

推荐答案

这里的BIG问题是setTimeout一旦启动就无法预测,特别是当它正在进行一些繁重的生命时。

The BIG problem here is setTimeout is unpredictable once it started, and especially when it is doing some heavy lifiting.

你可以在这里看到演示:
http://jsfiddle.net/wao20/C9WBg/

You can see the demo here: http://jsfiddle.net/wao20/C9WBg/

var secTmr = setTimeout(function(){
    $('#display').append('Timeout Cleared > ');
    clearTimeout(secTmr);        

    // this will always shown
    $('#display').append('I\'m still here! ');
}, 100);

您可以采取两项措施来尽量减少对浏览器性能的影响。

There are two things you can do to minimize the impact on the browser performance.

存储setTimeoutID的所有内容,并在您想要停止时循环播放

var timers = []

// When start the worker thread
timers.push( setTimeout(function () { sleep(1000);}, 1) );

// When you try to clear 
while (timers.length > 0) {
     clearTimeout(timers.pop());
}

当您尝试停止进程并检查该标志时设置一个标志在你的工作线程中,以防clearTimeout无法停止计时器

// Your flag 
var STOPForTheLoveOfGod = false;

// When you try to stop 
STOPForTheLoveOfGod = true; 
while (timers.length > 0) {       
     clearTimeout(timers.pop()); 
}

// Inside the for loop in the sleep function 
function sleep(milliseconds) {
    var start = new Date().getTime();
    for (var i = 0; i < 1e7; i++) {
        if (STOPForTheLoveOfGod) {
            break;
        }       

        // ...  
    }
}

您可以试用这个新脚本。
http://jsfiddle.net/wao20/7PPpS/4/

You can try out this new script. http://jsfiddle.net/wao20/7PPpS/4/

这篇关于Javascript:setTimeout和界面冻结的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆