我有多个长时间运行的任务,长度大于10毫秒,这会影响浏览器的响应能力。最糟糕的一些,例如从文件加载和解析3D模型,已经卸载到Web Workers,因此它们不会影响渲染循环。
然而,有些任务不容易移植到Workers,因此必须分布在主线程中的多个框架上。我不是一次性执行1秒任务,而是将其拆分为~5ms包,以使浏览器有机会在其间执行其他事件(鼠标移动,requestAnimationFrame,...)。
生成器函数与setTimeout结合使用似乎是最简单的方法。我已经在一起攻击了一些可以完成工作的东西,但我想知道是否有更好/更清洁的方法可以解决这个问题。
下面的代码计算了Math.random()的1亿次调用的平均值。 第一个版本一次计算平均值,但停止浏览器约1.3秒。 第二个版本滥用生成器函数以在每500万个点之后产生,从而使浏览器有机会在其间执行其他事件(鼠标移动)。通过setTimout循环重复调用生成器函数,直到它处理完所有1亿个样本。
<html>
<head></head>
<body>
<script>
let samples = 100 * 1000 * 1000;
{ // run complete task at once, possibly stalling the browser
function run(){
let start = performance.now();
let sum = 0.0;
for(let i = 0; i < samples; i++){
sum = sum + Math.random();
}
let mean = sum / samples;
let duration = performance.now() - start;
console.log(`single-run: duration: ${duration}`);
console.log(`single-run: sum: ${sum}`);
console.log(`single-run: mean: ${mean}`);
}
run();
}
{ // run in smaller packages to keep browser responsive
// move mouse to check if this callback is executed in between
document.body.addEventListener("mousemove", () => {
console.log("mouse moved");
});
function * distributedRun(){
let start = performance.now();
let packageSize = 5 * 1000 * 1000;
let sum = 0.0;
for(let i = 0; i < samples; i++){
sum = sum + Math.random();
if((i % packageSize) === 0){
yield sum;
}
}
let mean = sum / samples;
let duration = performance.now() - start;
console.log(`distributed-run: duration: ${duration}`);
console.log(`distributed-run: sum: ${sum}`);
console.log(`distributed-run: mean: ${mean}`);
yield sum;
}
let generatorInstance = distributedRun();
function loop(){
let result = generatorInstance.next();
console.log(`distributed-run intermediate result: ${result.value}`);
if(!result.done){
setTimeout(loop, 0);
}
}
loop();
}
</script>
</body>
</html>
ES2018有异步迭代器,这听起来像我正在寻找的那样,但我不确定它们是否真的意味着这类问题。像这样使用它仍然会拖延浏览器:
for await (const result of distributedRun()) {
...
}
(在这里和那里尝试了一些异步,并且在runDistributed()函数中,但是我仍然在学习await / async的详细信息)
答案 0 :(得分:0)
这是您的代码的稍作修改的版本。如果根据您的计算复杂度和可以允许的延迟量来调整chunk
,它应该可以正常工作。
let samples = 100 * 1000 * 1000;
let chunk = 100000;
async function run() {
let sum = 0.0;
for(let i=0; i<samples; i++) {
sum += Math.random();
if (i % chunk === 0) {
console.log("finished chunk")
// wait for the next tick
await new Promise(res => setTimeout(res, 0));
}
}
let mean = sum / samples;
console.log("finished computation", mean);
}
setTimeout(run, 0);