只有在for循环结束后才会调用回调
我通过websockets(paho-mqtt)在浏览器中接收数据,但问题是仅当另一个任务结束时才会触发接收回调(大循环),并且它被触发所有的堆积数据,我不会丢失数据只是延迟。即使有循环运行,该回调是不是应该被解雇?这里发生了什么?否则,我怎样才能做到这一点,在循环内继续接收?只有在for循环结束后才会调用回调
我想要说的是等效于以下内容:
如果我这样做是铬
setTimeout(() => {
console.log('hello!');
}, 10);
for (var i = 0; i < 50000; i++) {
console.log('for array');
}
我得到
50000 VM15292:5 for array
VM15292:2 hello!
我不应该得到这样的事情这个?
1000 VM15292:5 for array
VM15292:2 hello!
49000 VM15292:5 for array
其他人已确诊的问题很好,浏览器的单线程性质。我将提供一个可能的解决方案:发电机。
下面是这表明了问题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>
Javascript引擎往往是单线程的。
所以,如果你是在一个长期运行的紧密循环不产生(例如做一些IO),则回调将永远不会有机会运行,直到循环结束
当你运行JavaScript代码浏览器(除非使用Web Workers或其他特殊技术),它将在单个线程上执行。这听起来可能不太重要,但事实确实如此。
您的代码由for循环(同步)和setTimeout
(异步)调用组成。由于一次只能运行一段JavaScript,因此您的for-loop将永远不会被setTimeout
中断。
事实上,如果你的for循环包含在需要超过10毫秒即可完成非常密集的操作,您setTimeout
回调实际上可能是延迟过去那个标记,因为浏览器总是等待当前执行的代码完成然后继续运行事件循环。
setTimeout(() => {
console.log('hello!');
}, 10);
for (var i = 0; i < /* 50000 */ 5; i++) {
console.log('for array');
}