requestAnimationFrameでMaximum call stackのエラーが出る原因

requestAnimationFrameでコールスタック超過エラーが出る原因の図解

冬季休暇を利用してJavaScriptの勉強をしていたのですが、タイトルにも書いた↓のエラーに見事に嵌ってしまいました。。。

Uncaught RangeError: Maximum call stack size exceeded


300時間くらい悩んだ末に有意な解決策が見つかったので、後学のために此処に方法を共有します。
(嘘です。ただのポカミスです。)

解決法

普通は原因から始まると思いますが、まあ所詮ケアレスミスなのでサクッと解決法から書いていきます。

で、解決法なのですが、同じエラーが起きている人は、引数を以下のように指定してはいないでしょうか?

requestAnimationFrame(draw());


これを以下のように書き換えればこの問題は秒で解決するはずです。

requestAnimationFrame(draw);

※これで解決しない場合は、requestAnimationFrame以外の場所でスタック無限に積んでしまっている気がします。知らんけど。

原因

えっと、もう原因は火を見るよりも明らかに明らかですが、一応同じ過ちを繰り返さないために原因を見ていきます。

ズバリ原因は、

requestAnimationFrame()の引数指定が間違っていた!

ということです!!!!



…嘘です。もう少し掘り下げます。

まずは天下のmozilla様が公開しているドキュメントを見てみます。

window.requestAnimationFrame() メソッドは、ブラウザにアニメーションを行いたいことを知らせ、指定した関数を呼び出して次の再描画の前にアニメーションを更新することを要求します。このメソッドは、再描画の前に呼び出されるコールバック 1 個を引数として取ります。

MDN Web Docs

コールバック1個を引数として取る、とあります。

ここで誤っていたコードをもう一度見てみます。

// これは誤ったコードだよ

draw() {
    // フレーム毎に実行する色々な処理がここに来る

    // 次フレームを要求
    requestAnimationFrame(draw());
}

コールバックを引数にすべき(例えばここだとdrawと指定すべき)場所で、draw()と指定してしまっています。

これだと、draw()がここで実行されてその返り値がrequestAnimationFrame()に渡されることになってしまいます。
※本来はdrawをコールバック関数としてrequestAnimationFrame()さんに渡して「フレーム更新時に実行してね(はあと)」とすべき

これが諸悪の根源です。

なぜなら、draw()が実行されると、さらにその中でrequestAnimationFrame(draw())が実行されます。

すると、その中のdraw()がまた実行され、さらにその中で…となり、draw()が無限に実行される(スタックに積まれる)ことになるのです。

そして遂には、件のMaximum call stack size exceeded(=スタックサイズ超過してんぞワレ)のエラーが発生するのです。

requestAnimationFrameでコールスタック超過エラーが出る原因の図解

終わりに

最近めっきり寒くなりケアレ・スミスが止まりません。助けてください。

あ、これ2022年一発目の投稿ですね。あけましておめでとうございます。

参考

https://developer.mozilla.org/ja/docs/Web/API/Window/requestAnimationFrame

https://stackoverflow.com/questions/8771919/rangeerror-with-requestanimationframe

コメント

タイトルとURLをコピーしました