2017-03-09 50 views
1

我通过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 

回答

0

其他人已确诊的问题很好,浏览器的单线程性质。我将提供一个可能的解决方案:发电机。

下面是这表明了问题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> 
0

Javascript引擎往往是单线程的。

所以,如果你是在一个长期运行的紧密循环不产生(例如做一些IO),则回调将永远不会有机会运行,直到循环结束

3

当你运行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'); 
 
}