其他人已确诊的问题很好,浏览器的单线程性质。我将提供一个可能的解决方案:发电机。
下面是这表明了问题codepen: http://codepen.io/anon/pen/zZwXem?editors=1111
window.CP.PenTimer.MAX_TIME_IN_LOOP_WO_EXIT = 60000;
function log(message) {
const output = document.getElementById('output');
output.value = output.value + '\n' + message;
}
function asyncTask() {
log('Simulated websocket message')
}
function doWork() {
const timer = setInterval(1000, asyncTask);
let total = 0;
for (let i = 1; i < 100000000; i++) {
const foo = Math.log(i) * Math.sin(i);
total += foo;
}
log('The total is: '+ total);
clearInterval(timer);
}
当的doWork()方法是单击“做工作”按钮调用,从来没有的AsyncTask运行,而UI锁定。糟糕的用户体验。
以下示例使用生成器来运行长时间运行的任务。
http://codepen.io/anon/pen/jBmoPZ?editors=1111
//Basically disable codepen infinite loop detection, which is faulty for generators
window.CP.PenTimer.MAX_TIME_IN_LOOP_WO_EXIT = 120000;
let workTimer;
function log(message) {
const output = document.getElementById('output');
output.value = output.value + '\n' + message;
}
function asyncTask() {
log('Simulated websocket message')
}
let workGenerator = null;
function runWork() {
if (workGenerator === null) {
workGenerator = doWork();
}
const work = workGenerator.next();
if (work.done) {
log('The total is: '+ work.value);
workerGenerator = null;
} else {
workTimer = setTimeout(runWork,0);
}
}
function* doWork() {
const timer = setInterval(asyncTask,1000);
let total = 0;
for (let i = 1; i < 100000000; i++) {
if (i % 100000 === 0) {
yield;
}
if (i % 1000000 == 0) {
log((i/100000000 * 100).toFixed(1) + '% complete');
}
const foo = Math.log(i) * Math.sin(i);
total += foo;
}
clearInterval(timer);
return total;
}
在这里,我们在发电机不工作,并创建一个发电机转轮从“做工作”在UI按钮调用。这运行在最新版本的Chrome上,我不能说其他浏览器。通常情况下,您会使用类似babel的东西将生成器编译为生成构建的ES5语法。
生成器每计算10000行产生一次,并且每100000行发出一次状态更新。生成器runner'runWork'创建生成器的一个实例并重复调用next()。生成器然后运行,直到它碰到下一个“yield”或return语句。在生成器产生之后,生成器运行器通过调用setTimeout以0毫秒的方式放弃UI线程并将其本身用作处理函数。这通常意味着它将在每个动画帧(理想情况下)中调用一次。直到发电机返回完成标志,此时发电机转轮可以获得返回的值并清理。
这里的例子情况下,你需要重新创建codepen的HTML:
<input type='button' value='Do Work' onclick=doWork() />
<textarea id='output' style='width:200px;height:200px'></textarea>