「無限ループ防止」は「止まる出口を必ず用意する」という発想
タイマーやループを書いているときに一番やっちゃいけないのが、
「永遠に終わらない処理」=無限ループ です。
ブラウザが固まる
CPU が 100% になってファンが回り続ける
ページがまったく操作できなくなる
こうなると、ユーザーから見れば「バグってるサイト」です。
無限ループ防止の本質は、
「必ずどこかで止まる条件(出口)を用意しておく」 ことです。
特に、タイマー系(setInterval や requestAnimationFrame のループ)では、
「止める設計」を最初からセットで考えるのが超重要です。
まずは「典型的な無限ループ」のイメージを掴む
while や for の「出口がない」パターン
一番分かりやすい無限ループはこれです。
while (true) {
console.log("止まらない…");
}
JavaScript条件がずっと true なので、
永遠に終わりません。
もう少し現実的な「やらかし例」はこう。
let i = 0;
while (i < 10) {
console.log(i);
// i++ を書き忘れた!
}
JavaScripti が増えないので、i < 10 がずっと true のままです。
ここから学べるのは、
ループには
「条件」と「状態の変化」がセットで必要
状態が変わらないと、条件も変わらない
ということです。
タイマーでも「出口なしループ」は簡単に作れてしまう
タイマーを使ったループも、
設計をミスると「実質無限ループ」になります。
setInterval(() => {
console.log("ずっと動き続ける");
}, 1000);
JavaScriptこれ自体は悪くないですが、
「いつ止めるか」を考えていない という意味では、
「無限ループ予備軍」です。
requestAnimationFrame も同じです。
function loop() {
// 何かする
requestAnimationFrame(loop); // 永遠に呼び続ける
}
requestAnimationFrame(loop);
JavaScriptこれも「止める条件」がなければ、
ずっと動き続けます。
タイマー系で無限ループを防ぐ基本パターン
setInterval は「必ず clearInterval とペアで考える」
setInterval を使うときは、
「どこで clearInterval するか」 を必ずセットで考えます。
例えば、10 回だけ実行したいならこう。
let count = 0;
const id = setInterval(() => {
count++;
console.log(count);
if (count >= 10) {
clearInterval(id); // ここが出口
}
}, 1000);
JavaScriptここで重要なのは、
「いつ止めるか」をコードの中に明示している
条件が満たされたら必ず clearInterval が呼ばれる
という「出口の設計」です。
requestAnimationFrame も「終了条件」を必ず書く
アニメーションのループも同じです。
let x = 0;
function animate() {
x += 2;
box.style.left = `${x}px`;
if (x < 300) {
requestAnimationFrame(animate); // まだ動かす
} else {
// ここで終了(次の requestAnimationFrame を呼ばない)
}
}
requestAnimationFrame(animate);
JavaScriptrequestAnimationFrame のループは、
「次のフレームを予約するかどうか」を
自分で決められる
というのがポイントです。
「もう動かさなくていい」と判断したら、
次の requestAnimationFrame を呼ばない。
これが出口になります。
「回数で止める」「時間で止める」「状態で止める」
回数で止める(カウンタを使う)
一番シンプルなのは「何回まで」と決める方法です。
let count = 0;
const max = 5;
const id = setInterval(() => {
count++;
console.log(`実行 ${count} 回目`);
if (count >= max) {
clearInterval(id);
}
}, 1000);
JavaScriptこのパターンは、
リトライ処理(最大 3 回まで試す)
一定回数だけアニメーションする
などにそのまま使えます。
時間で止める(開始時刻からの経過で判断)
「◯秒経ったら止める」というパターンもよく使います。
const start = Date.now();
const limit = 5000; // 5秒
const id = setInterval(() => {
const elapsed = Date.now() - start;
console.log(`経過: ${elapsed} ms`);
if (elapsed >= limit) {
clearInterval(id);
console.log("5秒経ったので終了");
}
}, 200);
JavaScriptここでは、
タイマーの回数ではなく
「本当の経過時間」で止める
という設計になっています。
状態で止める(フラグや条件で制御)
「ある条件が満たされたら止める」というのもよくあります。
let running = true;
function loop() {
if (!running) return; // ここが出口
console.log("ループ中…");
requestAnimationFrame(loop);
}
requestAnimationFrame(loop);
// どこかのタイミングで
running = false;
JavaScriptこのように、
「フラグを見てループを抜ける」 という形にしておくと、
外からも止めやすくなります。
「無限ループを作らないための思考のクセ」
ループを書く前に「いつ終わるか」を言語化する
コードを書く前に、
自分にこう問いかけてみてください。
「この処理は、どんな条件で終わるべき?」
例えば、
10 回実行したら終わる
300px まで動いたら終わる
5 秒経ったら終わる
成功したら終わる(成功するまでリトライ)
など、「終わりの条件」を日本語で言えるかどうか が大事です。
言葉にできたら、それをそのままコードに落とします。
「10 回実行したら終わる」
→ count >= 10 なら clearInterval
「300px まで動いたら終わる」
→ x >= 300 なら次の requestAnimationFrame を呼ばない
この「出口を先に決める」クセが、
無限ループ防止の一番の武器になります。
「状態が変わらないループ」は危険信号
ループの中で、
ループ条件に関係する変数が変化していない ときは要注意です。
let i = 0;
while (i < 10) {
console.log(i);
// i を変えていない → 危険
}
JavaScriptタイマーでも同じで、
フラグを見ているのに、そのフラグをどこでも変えていない
カウンタを見ているのに、カウンタを増やしていない
こういうコードは、
「出口がないかもしれない」と疑ってください。
実際に「危ないコード」を安全に書き換えてみる
危ない例:setInterval を止めることを考えていない
setInterval(() => {
console.log("サーバーに状態を取りに行く");
// fetch("/api/status") ...
}, 1000);
JavaScriptこれだと、
ページを離れても、
コンポーネントが破棄されても、
永遠にリクエストを送り続けます。
安全な形に書き換えるとこうなります。
let timerId = null;
function startPolling() {
if (timerId !== null) return;
timerId = setInterval(() => {
console.log("サーバーに状態を取りに行く");
}, 1000);
}
function stopPolling() {
if (timerId !== null) {
clearInterval(timerId);
timerId = null;
}
}
JavaScriptこれで、
必要なときだけ startPolling()
不要になったら stopPolling()
という「開始」と「終了」がセットになります。
危ない例:requestAnimationFrame を永遠に呼び続ける
function loop() {
// 何か重い処理
requestAnimationFrame(loop);
}
requestAnimationFrame(loop);
JavaScriptこれも、
止める手段がないループ です。
終了条件を入れるとこうなります。
let running = true;
let frameCount = 0;
function loop() {
if (!running) return;
frameCount++;
console.log(`frame: ${frameCount}`);
if (frameCount >= 300) {
running = false; // 300フレームで終了
return;
}
requestAnimationFrame(loop);
}
requestAnimationFrame(loop);
JavaScriptここでは、
フラグ(running)
カウンタ(frameCount)
を使って、
「どこで終わるか」を明示しています。
初心者として「無限ループ防止」で本当に掴んでほしいこと
無限ループ防止の本質は「必ず出口を用意する」こと
setInterval は必ず clearInterval とペアで設計する(どこで止めるかを先に決める)
requestAnimationFrame のループも「次を予約しない」という形で終了条件を書く
回数・時間・状態(フラグ)など、何をもとに「終わり」を判断するかをコードに落とす
ループを書く前に「この処理はどんな条件で終わるべき?」と自分に問いかけるクセをつける
小さな練習として、
1 秒ごとに数字を増やすタイマー
→ 10 まで行ったら止める
→ ボタンを押したら途中でも止められる
みたいなコードを書いてみてください。
「動き続けるものには、必ず止まる条件をつける」
この感覚が一度身体に入ると、
タイマーでもループでも、“暴走しないコード” を自然に書けるようになります。
