setTimeout と setInterval
このレッスンで分かること
setTimeout(関数, ミリ秒)は「あとで 1 回だけ呼んでね」という予約ですsetInterval(関数, ミリ秒)はclearIntervalで止めるまで呼び続けます
3 秒待たせると、その 3 秒だけページが死ぬ
ここまで書いてきたコードは、上から下へ 1 行ずつ動いてきました。JavaScript には作業員が 1 人しかいない、と考えると分かりやすいです。この作業員は、手に持っている仕事をやり切るまで、ほかの仕事を始められません。
だから「3 秒たったらお知らせを消す」を素直にやろうとして、作業員をその場で 3 秒立ち止まらせると、その 3 秒のあいだページは完全に固まります。ボタンを押しても反応せず、文字も選べません。
そこで使うのが予約という考え方です。「3 秒たったら、この紙に書いた仕事をやってね」と伝えて、自分はすぐ次の行へ進みます。3 秒後、手が空いたときに作業員が紙を拾います。
JavaScript
setTimeout(function () {
console.log("3 秒たちました");
}, 3000);第 1 引数に「あとでやってほしい処理」を関数のまま渡し、第 2 引数に待つ長さをミリ秒で書きます。1 秒なら 1000、0.5 秒なら 500 です。ここを 3 と書くと 3 ミリ秒後、つまりほぼ一瞬で動いてしまうので気を付けてください。
書いた順と、動く順が違う
setTimeout を書いた行で処理が止まるわけではない、というのが最初のつまずきどころです。
JavaScript
console.log("1 番目");
setTimeout(function () {
console.log("2 番目のつもり");
}, 1000);
console.log("3 番目のつもり");コンソールに出る順は 1 番目 3 番目のつもり 2 番目のつもり です。予約した処理は、いま動いている流れが終わってから呼ばれるので、下に書いたほうが先に出ます。「1000 ミリ秒後ぴったり」ではなく「1000 ミリ秒たった以降で、手が空いたとき」と覚えておくと、時刻がわずかにずれても慌てずにすみます。
setInterval は書き方がそっくりですが、1 回で終わらず、指定した間隔でずっと呼び続けます。時計のように決まった間隔で表示を新しくしたいときに使います。仕掛けたい処理は先に関数として用意しておき、名前だけを渡します。
| 関数 | 何をするか | 止め方 |
|---|---|---|
| setTimeout | 指定した時間のあと 1 回だけ呼ぶ | clearTimeout(番号) |
| setInterval | 指定した間隔でくり返し呼ぶ | clearInterval(番号) |
止めないと、ページを離れても動き続ける
setInterval の戻り値は、その予約に付けられた番号です。この番号を変数に取っておかないと、あとで止める手段がありません。
JavaScript
let count = 0;
const timerId = setInterval(function () {
count = count + 1;
console.log(count + " 回目");
if (count === 5) {
clearInterval(timerId);
}
}, 1000);止め忘れは現場で本当によく起きます。1 秒ごとに動く処理が 10 個たまれば、ページは 1 秒ごとに 10 回の仕事を抱えることになり、スクロールも入力ももたつきはじめます。要素を消したのに、その要素を書き換えようとする予約だけが残って、毎秒エラーを吐き続けることもあります。役目が終わったら必ず止める、と決めておいてください。使い終わった予約が残り続けて動きが鈍っていく状態を、メモリリークと呼びます。
setTimeout のほうも clearTimeout で取り消せます。対象はまだ動いていない予約だけで、すでに実行された予約を取り消すことはできません。
なお、setInterval を仕掛けても、最初の 1 回は指定した時間がたつまで動きません。読み込んだ直後から表示を出したいなら、仕掛ける前に同じ関数を自分で 1 回呼んでおきます。細かい話に見えますが、この一瞬のちらつきの有無が、できあがりの印象を大きく変えます。
よくある間違い
- かっこを付けて呼んでしまう —
setInterval(reload(), 1000)はreload()をその場で実行し、その戻り値を予約しています。渡すのは関数そのものなので、かっこは付けません - 戻り値を受け取らない — 番号を変数に入れておかないと
clearIntervalに渡すものがなく、二度と止められません - 秒とミリ秒を取り違える —
setTimeout(fn, 3)は 3 秒後ではなく 3 ミリ秒後です。3 秒待たせたいなら3000と書きます
課題
- ヘッダーに、時刻を出す id が clock の p と、止める id が clock-stop の button と、状態を出す id が clock-status の p と、お知らせ用の id が notice の p を足す
- showTime 関数を作り、clock に時刻を入れる。読み込んだ直後に 1 回呼んでおく
- setInterval で 1 秒ごとに showTime を呼び、戻り値の番号を変数に入れておく
- clock-stop を押したら clearInterval で止め、clock-status に停止中と分かる文を入れる
- setTimeout を 1 つ使い、notice に出したお知らせを 5 秒後に消す