Javascript 只有在big for循环结束后才调用回调
我通过websockets(paho mqtt)在浏览器中接收数据,但问题是,只有在另一个任务结束(big for loop)时才会触发接收回调,并且它会触发所有堆叠的数据,我不会因为延迟而丢失数据。即使有一个循环在运行,回调不应该被触发吗?这里发生了什么事?。否则,我如何实现这一点,在循环中保持接收 我想说的相当于以下内容: 如果我用chrome做这个Javascript 只有在big for循环结束后才调用回调,javascript,web,browser,mqtt,paho,Javascript,Web,Browser,Mqtt,Paho,我通过websockets(paho mqtt)在浏览器中接收数据,但问题是,只有在另一个任务结束(big for loop)时才会触发接收回调,并且它会触发所有堆叠的数据,我不会因为延迟而丢失数据。即使有一个循环在运行,回调不应该被触发吗?这里发生了什么事?。否则,我如何实现这一点,在循环中保持接收 我想说的相当于以下内容: 如果我用chrome做这个 setTimeout(() => { console.log('hello!'); }, 10); for (var i = 0;
setTimeout(() => {
console.log('hello!');
}, 10);
for (var i = 0; i < 50000; i++) {
console.log('for array');
}
我不应该买这样的东西吗
1000 VM15292:5 for array
VM15292:2 hello!
49000 VM15292:5 for array
Javascript引擎往往是单线程的
因此,如果您处于一个长时间运行的紧密循环中,而该循环不产生(例如,执行一些io),那么回调将永远不会有机会运行,直到在浏览器中运行JavaScript代码时循环完成为止(除非使用或其他特殊技术),回调将在单个线程上执行。这听起来可能不太重要,但确实如此 您的代码由for循环(同步)和对
setTimeout
(异步)的调用组成。因为一次只能运行一段JavaScript,所以for循环永远不会被setTimeout
中断
事实上,如果for循环包含非常密集的操作,需要超过10 ms才能完成,那么setTimeout
回调实际上可能会延迟超过该标记,因为浏览器在继续运行事件循环之前总是等待当前执行的代码完成
setTimeout(()=>{
console.log('hello!');
}, 10);
对于(变量i=0;i*50000*/5;i++){
log('for array');
}
其他人已经很好地诊断了这个问题,即浏览器的单线程特性。我将提供一个可能的解决方案:发电机
下面是一个代码笔,它演示了问题:
window.CP.PenTimer.MAX_TIME_IN_LOOP_WO_EXIT=60000;
功能日志(消息){
const output=document.getElementById('output');
output.value=output.value+'\n'+消息;
}
函数asyncTask(){
日志('模拟websocket消息')
}
函数doWork(){
常量计时器=设置间隔(1000,异步任务);
设total=0;
对于(设i=1;i<100000000;i++){
const foo=Math.log(i)*Math.sin(i);
总数+=foo;
}
日志('总计为:'+总计);
清除间隔(计时器);
}
当通过单击“Do Work”按钮调用doWork()时,asyncTask将永远不会运行,UI将锁定。可怕的UX
以下示例使用生成器运行长时间运行的任务
//基本上禁用codepen无限循环检测,这对于生成器来说是错误的
window.CP.PenTimer.MAX_TIME_IN_LOOP_WO_EXIT=120000;
让工作计时器;
功能日志(消息){
const output=document.getElementById('output');
output.value=output.value+'\n'+消息;
}
函数asyncTask(){
日志('模拟websocket消息')
}
让workGenerator=null;
函数运行工作(){
如果(工作生成器===null){
workGenerator=doWork();
}
const work=workGenerator.next();
如果(工作完成){
log('总计为:'+工时值);
workerGenerator=null;
}否则{
workTimer=setTimeout(runWork,0);
}
}
函数*doWork(){
常量计时器=设置间隔(异步任务,1000);
设total=0;
对于(设i=1;i<100000000;i++){
如果(i%100000==0){
产量
}
如果(i%1000000==0){
日志((i/100000000*100).toFixed(1)+'%complete');
}
const foo=Math.log(i)*Math.sin(i);
总数+=foo;
}
清除间隔(计时器);
返回总数;
}
在这里,我们在生成器中执行工作,并创建一个生成器运行程序,以便从UI中的“执行工作”按钮调用。这是在最新版本的Chrome上运行的,我不能为其他浏览器说话。通常,您会使用babel之类的工具将生成器编译为生产构建的ES5语法
生成器每10000行生成一次计算,并每100000行发出一次状态更新。生成器运行程序“runWork”创建生成器的实例并重复调用next()。然后,生成器一直运行,直到到达下一个“yield”或return语句。生成器生成后,生成器运行程序将在0毫秒内调用setTimeout并将自身用作处理程序函数,从而放弃UI线程。这通常意味着它将在每个动画帧(理想情况下)调用一次。直到生成器返回done标志,此时生成器运行程序可以获取返回的值并进行清理
下面是需要重新创建代码笔时的HTML示例:
<input type='button' value='Do Work' onclick=doWork() />
<textarea id='output' style='width:200px;height:200px'></textarea>
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);
}
//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;
}
<input type='button' value='Do Work' onclick=doWork() />
<textarea id='output' style='width:200px;height:200px'></textarea>