やりかけのことがあって、なんかハマちゃってて抜け出せないんで寄り道。
「知らなくて調べた」わけではなく、「タイマーは setInterval だったよな、確か」「殺すのはなんだっけ、…javascript△clear、あ、clearInterval か」てな具合。後者でどういうわけだか「効かない」が suggestion として候補にあがる。
何箇所かみたんだけれども、「効かないことにハマった人がほかにもいるようだ」「setInterval したもの全部クリアすればよい」で話を終えてる。
正直「なんぢゃそりゃぁ」と思った。ほかにも考えることあんじゃないの、とか、「当たり前じゃないか」とか。こんなもん「javascript」関係ない。
ボタンやら hover がトリガーになってタイマー起動するんなら二度目の起動をしないように disabled にするとかさ、普通に考えるんじゃないのかね。とか。
あとね、皆「タイマーつぅたら一定時間間隔で繰り返すもんだ」と思い込みがちなんだけれども、その考え方もちょっと捨ててみるといい。setInterval もそうだけれども、「タイマー的なことを提供出来る API」のほとんど全ては、「一定時間で指定処理を繰り返す、もしくは指定時間後に指定処理を呼び出す」の両方のニーズに耐えられるように必ず作られてる(というか一方があれば他方は必ず実現出来る)。
後者のような使い方をワタシは「ワンショットタイマー」と呼ぶのを好むが、まぁ呼び名はなんでもいい。そして後者は「setInterval 前に必ず clearInterval する」とか、setInterval で呼び出されるコールバック内で clearInterval するとかそんなやり方でやったりする。あるいは「一定時間間隔で」ではなく「可変時間間隔で」を実現するのにも、「setInterval で呼び出されるコールバック内で clearInterval/setInterval し直す」でいい。…みたいな発想を日常からしてるとだな、「setInterval を何度も呼び出すと clearInterval が一回分にしか効かないので止まりません! 注意です!」みたいな注意(情報)って、なんだかなぁ、としか思えない。
「ワンショットタイマー」の使い方でワタシが好きなのは「タイムアウトの代わりに使う」使い方。発想はこう:
- 何かイベント発火したらワンショットタイマー起動
- 連続で立て続けに同じイベント発火したら前のタイマー殺しつつ再びワンショットタイマー起動
- …以下同文…
- 発火が「落ち着いた」らめでたく最後に生きていたワンショットタイマーのコールバック起動
猛烈な勢いでやってくる何かイベントの最後のものだけ採用すればいい場合に使う。たとえばフォルダの監視なんかしてるとする。この場合に大量ファイルが追加や削除された一つ一つのイベントにいちいち反応してたら埒が明かない。忙しいイベントが落ち着いてからじっくり反応すればいいのだ。(確か kivyLauncher ネタのどれかで使ってた気がすんだけど覚えてない。かなり日常なので。)
なんつーのかな、この手の発想を普段からしてる場合は「setTimer(的な何か)/killTimer(的な何か)」は必ず対で考えるんだよね。「setInterval を何度も呼び出すと clearInterval が一回分にしか効かないので止まりません! 注意です!」的思考で止まる人たちは、脳内でこれらがペアになってないんだと思う。
2022-04-30追記:
人の批判をすれば自分に跳ね返ってくる、なんてことは、いつだって頭の片隅にはあれど、それでも喰らえば反省はする。
「止まりません! 注意です!」は、ブラウザと OS の進化具合によってその深刻度合いは都度違う。本日時点での Chrome と Windows 11 という状態だと、「Windows 全体がフリーズ」に近い状態に陥る。「Chrome が」じゃないよ。Windows 11 (とたぶん10も)はなんかプロセスのプライオリティ管理がちょっと退化したんじゃないかなと思うんだよな、以前よりもアプリケーション個々の「応答がありません」がシステム全体に影響する割合が確実に増えてる。だからこのミスをやらかすと、信じられない時間のロスになってしまう。(諦めて OS リブートまで含むケースすらあるから。)
そもそも「javascript のオフラインマニュアル」があればいいのに、と思うんだよね、「ワタシはたまにしか javascript にご用じゃない → たいがいのことは調べなおさないと思い出さない → setInterval/clearInterval の仕様も大抵忘れてる → のにオフラインマニュアルがないことで調べるのをサボりがち」というバッドサイクルが「今日の悲劇」を生んでしまった。今ハングアップから復帰したとこ。10分以上の浪費。
「elapse は秒だっけ、ミリ秒だっけ」みたいなことすら忘れてるので「やってみりゃわかるか」というトライアルアンドエラー要素が多いことがトリガーとなって「clearInterval し忘れた(というかタイマーIDを記憶し忘れた)」というのが今回のミスの構造なんだけれど、要はこういうの、コードのテンプレートがありゃぁいいんだよね、ワタシが最も多用するパターンだとこれ:
1 var timerId = null;
2 function mytimerfun() {
3 /* ... */
4 if (timerId != null) {
5 clearInterval(timerId);
6 timerId = null;
7 }
8 if (cond) {
9 timerId = setInterval(function () {
10 mytimerfun();
11 }, 10000); /* 10secs */
12 }
13 }
「脳内でペア」になってたって「timerId = とタイプし忘れる」ことはある。そういうこと。