他の人が、うまくブラウザのシングルスレッドの性質を、問題を診断しています。私は可能な解決策を提供します:発電機。ここで
は、問題を示しcodepenです:doWorkは()「する仕事」ボタンをクリックすることで呼び出されたとき 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);
}
、asyncTaskが実行されることはありませんし、UIはロックアップ。恐ろしいUX。
次の例では、長期実行タスクを実行するためにジェネレータを使用しています。
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の最新バージョンで実行されますが、私は他のブラウザでは話すことができません。典型的には、プロダクションビルド用のES5構文にジェネレータをコンパイルするには、babelのようなものを使用します。
ジェネレータは、10000行の計算ごとに生成され、100000行ごとにステータス更新を発行します。ジェネレータランナー '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>