使用 setTimeout 了解异步
Understanding async using setTimeout
我有一个 UI,我需要动画才能流畅运行。每隔一段时间,我需要做一个半大数据计算,使动画跳过,直到这个计算完成。
我试图通过使数据计算与setTimeout
异步来解决此问题。类似setTimeout(calcData(), 0);
整个代码是这样的(简化):
while (animating) {
performAnimation();
if (needCalc) {
setTimeout(calcData(), 0);
}
}
但我仍然在动画中跳过了。当我不需要进行任何数据计算时,它运行流畅。我怎样才能有效地做到这一点?谢谢!
您看到跳过是因为一次只运行一个 JavaScript 线程。当异步完成某些事情时,javascript 引擎会将其放入一个队列以供稍后运行,然后找到其他要执行的内容。当队列中的某些内容需要完成时,引擎会将其拉出并执行,阻止所有其他操作,直到完成。然后,引擎从其队列中提取其他内容来执行。
因此,如果要让渲染顺利运行,则必须将计算分解为多个异步调用,从而允许引擎在两次计算之间安排渲染操作。如果您只是遍历数组,这很容易实现,因此您可以执行以下操作:
var now=Date.now;
if(window.performance&&performance.now){//use performace.now if we can
now=performance.now;
}
function calculate(){
var batchSize=10;//If you have a exceptionally long operation you may want to make this lower.
var i=0;
var next=function(){
var start=now();
while(now()-start<14){//14ms / frame
var end=Math.min(i+batchSize,data.length);
for(;i<end;i++){//do batches to reduce time overhead
do_calc(data[i]);
}
}
if(i<data.length) setTimeout(next,1)//defer to next tick
};
next();
}
calculate();
function render(){
do_render_stuff();
if(animating) {
requestAnimationFrame(render);//use requestAnimationFrame rather then setTimeout for rendering
}
}
render();
更好的是,如果可以的话,你应该使用WebWorkers,它在不同的线程中工作,与主js引擎完全分开。但是,如果您需要做一些在 WebWorker 中无法做的事情,例如操作 DOM 树,那么您就会陷入困境。
首先,让我们谈谈你的代码中发生了什么:
while (animating) {
performAnimation();
if (needCalc) {
// it should be setTimeout(calcData, 0);
setTimeout(calcData(), 0);
}
}
实际上setTimeout(calcData(), 0);
您不会延迟调用calcData
函数,而是调用它,因为您在函数名称后使用()
运算符。
其次,让我们想想,当你在上面的代码中真正对calcData
进行延迟调用时会发生什么:通常 JavaScript 在一个线程中运行,所以,如果你有这样的代码:
setTimeout(doSomething, 0);
while (true) {};
doSomething
永远不会被调用,因为javascript的解释器永远执行循环while
并且它没有"空闲时间"来执行其他事情(甚至是UI)。 setTimeout
- 只需说在解释器空闲时安排执行doSomething
,是时候执行此功能了。
因此,当浏览器执行javascript函数时,所有其他内容都会冻结。
解决方案:
如果你有需要处理的大数据,也许最好在后端进行计算,然后将结果发送到前端。
通常,当您需要进行一些计算并呈现结果时,最好使用 requestAnimationFrame 而不是循环
while
。浏览器会尽快执行传入requestAnimationFrame
函数,但你也会给浏览器一个时间来处理其他事件。您可以使用游戏requestAnimationFrame
看到流畅的重绘(此处为分步教程)。如果你真的想在前端部分处理大量的数据,并且你想让 ui 工作顺利,你可以尝试使用 WebWorkers。WebWorker 看起来像 JavaScript 中的线程,您需要通过将消息从一个传递到另一个并返回来在主 UI "线程"和 WebWorker 之间进行通信,并且 WebWorker 上的计算不会影响 UI 线程。
大多数情况下,您的问题归结为您对 setTimeout() 的错误使用
setTimeout(calcData(), 0);
setTimeout 的第一个参数是对要调用的函数的引用。在您的代码中,您没有引用 calcData 函数,而是调用它,因为您在函数名称后包含 ()。
其次,您为延迟输入 0 并不意味着在函数运行之前会有 0 秒的延迟。JavaScript 在单线程上下文中运行。setTimeout 函数被放置在队列中,并在 JavaScript 引擎可用时执行,但不得早于最小 10 毫秒或您指定的量(以较小者为准)。
实际上,您的行应该是:
setTimeout(calcData(),10);
- esri javascript异步打印
- JavaScript异步问题
- $translateProvider.useStaticFilesLoader的Angular Translate异步定
- 异步facebook功能
- 异步并行错误
- 在Redux中,我应该在哪里编写复杂的异步流
- 如何在Analytics.js中始终了解最新的cookie过期时间
- 角度异步http自动完成
- 如何从SeleniumWebdriver获取异步Javascript响应
- 如何使用异步调用更改工厂的变量
- 了解双击代码标记
- 在等待异步任务时永久循环
- 如何在异步函数中使用javascript对象
- 调用后不异步Ajax忽略函数
- 了解Javascript异步调用
- 了解在Selenium中执行异步脚本
- 使用 setTimeout 了解异步
- 了解node.js中的异步函数
- 了解异步回调
- 了解异步语句的变量作用域