只有在大 for 循环结束后才会调用回调
Callback is being called only after big for loop ends
我正在通过 websockets (paho-mqtt) 在浏览器中接收数据,但问题是接收回调仅在另一个任务结束时被触发(大 for 循环)并且它被所有堆叠数据触发,我'我不会丢失数据,只是会延迟。即使有一个循环 运行 也不应该触发回调吗?这里发生了什么?否则,我如何实现这一点,在循环中不断接收?
我想说的相当于以下内容:
如果我在 chrome
中这样做
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
Javascript 引擎趋向于单线程。
因此,如果您处于 运行 不屈服的紧密循环中(例如,执行一些 io),那么在循环完成之前,回调永远不会有机会 运行
当您在浏览器中运行 JavaScript 编写代码时(除非使用Web Workers 或其他特殊技术),它是在单线程上执行的。这听起来可能不太重要,但确实如此。
您的代码包含一个 for 循环(同步)和一个对 setTimeout
的调用(异步)。由于一次只能 JavaScript 中的一个 运行ning,因此您的 for 循环将永远不会被 setTimeout
.
打断
事实上,如果您的 for 循环包含需要超过 10 毫秒才能完成的极其密集的操作,您的 setTimeout
回调实际上可能会 延迟 超过该标记,因为浏览器在继续 运行 事件循环之前总是等待当前执行的代码完成。
setTimeout(() => {
console.log('hello!');
}, 10);
for (var i = 0; i < /* 50000 */ 5; i++) {
console.log('for array');
}
其他人已经很好地诊断了问题,浏览器的单线程特性。我将提供一个可能的解决方案:发电机。
这是演示问题的代码笔:
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);
}
当通过单击 'Do Work' 按钮调用 doWork() 时,asyncTask 永远不会 运行s,并且 UI 会锁定。糟糕的用户体验。
以下示例使用生成器来 运行 长 运行ning 任务。
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;
}
这里我们在生成器中工作,并创建一个生成器 运行ner 以从 UI 中的 'Do Work' 按钮调用。这个运行s在最新版本Chrome上,我不能代表其他浏览器。通常,您会使用 babel 之类的工具将生成器编译为 ES5 语法以用于生产构建。
生成器每 10000 行计算产生一次,并且每 100000 行发出一次状态更新。生成器 运行ner 'runWork' 创建生成器的一个实例并重复调用 next()。然后生成器 运行s 直到它命中下一个 'yield' 或 return 语句。在生成器 yield 之后,生成器 运行ner 然后通过调用 0 毫秒的 setTimeout 并将其自身用作处理函数来放弃 UI 线程。这通常意味着它将在每个动画帧(理想情况下)被调用一次。这一直持续到生成器 returns 完成标志,此时生成器 运行ner 可以获得 returned 值并清理。
这里是 HTML 示例,以防您需要重新创建代码笔:
<input type='button' value='Do Work' onclick=doWork() />
<textarea id='output' style='width:200px;height:200px'></textarea>
我正在通过 websockets (paho-mqtt) 在浏览器中接收数据,但问题是接收回调仅在另一个任务结束时被触发(大 for 循环)并且它被所有堆叠数据触发,我'我不会丢失数据,只是会延迟。即使有一个循环 运行 也不应该触发回调吗?这里发生了什么?否则,我如何实现这一点,在循环中不断接收?
我想说的相当于以下内容:
如果我在 chrome
中这样做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
Javascript 引擎趋向于单线程。
因此,如果您处于 运行 不屈服的紧密循环中(例如,执行一些 io),那么在循环完成之前,回调永远不会有机会 运行
当您在浏览器中运行 JavaScript 编写代码时(除非使用Web Workers 或其他特殊技术),它是在单线程上执行的。这听起来可能不太重要,但确实如此。
您的代码包含一个 for 循环(同步)和一个对 setTimeout
的调用(异步)。由于一次只能 JavaScript 中的一个 运行ning,因此您的 for 循环将永远不会被 setTimeout
.
事实上,如果您的 for 循环包含需要超过 10 毫秒才能完成的极其密集的操作,您的 setTimeout
回调实际上可能会 延迟 超过该标记,因为浏览器在继续 运行 事件循环之前总是等待当前执行的代码完成。
setTimeout(() => {
console.log('hello!');
}, 10);
for (var i = 0; i < /* 50000 */ 5; i++) {
console.log('for array');
}
其他人已经很好地诊断了问题,浏览器的单线程特性。我将提供一个可能的解决方案:发电机。
这是演示问题的代码笔: 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);
}
当通过单击 'Do Work' 按钮调用 doWork() 时,asyncTask 永远不会 运行s,并且 UI 会锁定。糟糕的用户体验。
以下示例使用生成器来 运行 长 运行ning 任务。
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;
}
这里我们在生成器中工作,并创建一个生成器 运行ner 以从 UI 中的 'Do Work' 按钮调用。这个运行s在最新版本Chrome上,我不能代表其他浏览器。通常,您会使用 babel 之类的工具将生成器编译为 ES5 语法以用于生产构建。
生成器每 10000 行计算产生一次,并且每 100000 行发出一次状态更新。生成器 运行ner 'runWork' 创建生成器的一个实例并重复调用 next()。然后生成器 运行s 直到它命中下一个 'yield' 或 return 语句。在生成器 yield 之后,生成器 运行ner 然后通过调用 0 毫秒的 setTimeout 并将其自身用作处理函数来放弃 UI 线程。这通常意味着它将在每个动画帧(理想情况下)被调用一次。这一直持续到生成器 returns 完成标志,此时生成器 运行ner 可以获得 returned 值并清理。
这里是 HTML 示例,以防您需要重新创建代码笔:
<input type='button' value='Do Work' onclick=doWork() />
<textarea id='output' style='width:200px;height:200px'></textarea>