Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/399.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 只有在big for循环结束后才调用回调_Javascript_Web_Browser_Mqtt_Paho - Fatal编程技术网

Javascript 只有在big for循环结束后才调用回调

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;

我通过websockets(paho mqtt)在浏览器中接收数据,但问题是,只有在另一个任务结束(big for loop)时才会触发接收回调,并且它会触发所有堆叠的数据,我不会因为延迟而丢失数据。即使有一个循环在运行,回调不应该被触发吗?这里发生了什么事?。否则,我如何实现这一点,在循环中保持接收

我想说的相当于以下内容:

如果我用chrome做这个

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}
其他人已经很好地诊断了这个问题,即浏览器的单线程特性。我将提供一个可能的解决方案:发电机

下面是一个代码笔,它演示了问题:

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>