キャンバスに描画する機能があります。この関数はいくつかの手間のかかる作業を行います。私が理解しているように、requestAnimationFrame()は呼び出しスタックをクリア/リセットしないため、この手間のかかる作業の変数はメモリに保持されます。
最近のFirefoxのアップデートでこの問題が発生しました(数秒以内に10GBを超えるRAMが使用されました)。以前のバージョンのFirefoxで動作し、Chromeでも動作するため、バグの可能性があります。しかし、それでも、これを自分で修正する方法や、より良いコードを書く方法があるかどうかを知りたいと思います。
前もって感謝します!
コード例:
"use strict";
class DrawUnit {
constructor(canvas){
this.canvas = canvas;
window.requestAnimationFrame(()=>{
this.draw()
});
}
draw(){
let data = generateAndProcessData(); //heavy lifting happening here, old data stays in memory because the previously called draw() still has a reference to it right?
let ctx = this.canvas.getContext('2d');
//reset canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
for(let d of data){
//draw some line
}
window.requestAnimationFrame(()=>{
this.draw()
})
}
}
setTimeout()は同じ結果を提供しますが、私はrequestAnimationFrame()を使用することを好みます
問題と解決策は5行目の無名関数に関連していると思いますが、this.draw()
自分で解決策を見つけることができませんでした。匿名関数を使用しない場合、実行コンテキストが失われますが、それが必要です!
draw
実際には再帰的ではありません。Firefoxのコールスタックは、デバッグ目的の非同期ネストを含む概念的なコールスタックを示しています。実際のコールスタックは、draw
戻るときにクリアされます。
渡すために作成しているクロージャーrequestAnimationFrame
は、理論data
的には、クローズする字句環境オブジェクトを介して間接的に参照を保持します。ブラウザはそのリンクを最適化できる場合とできない場合があります(ChromeなどのJavaScriptエンジンであるV8は、クロージャの最適化にかなり積極的でしたが、実行時間の影響のために、完全ではなく少し後退していました。それがあった)。
良いニュースは、以下を使用することで閉鎖を完全に回避できることbind
です。
requestAnimationFrame(this.draw.bind(this));
bind
呼び出されると、最初の引数がthis
何であれ、元の関数を設定して呼び出す新しい関数を作成しますbind
。現在の状況では、クロージャは作成されません。
したがって、への呼び出し中に作成されたクロージャがないdraw
ためdata
、以前の呼び出しから何も保持されません(最適化がない場合)。
requestAnimationFrame
ただし、あなたが与えるコールバックは決して重労働であってはなりません。非常に迅速に作業を行う必要があります。(rAF
コールバックに数ミリ秒以上かかる場合、ブラウザーはあなたに不平を言います。)
「重いリフティング」がCPU時間の点で重要な場合は、可能であればWebワーカーにオフロードpostMessage
し、DOMにレンダリングするために最終データをメインスレッド()に送信することをお勧めします。message
イベントを処理するメインスレッドのコードは、次にペイントするときにブラウザによってペイントされるように、イベントをDOMにレンダリングします。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加