Help us understand the problem. What is going on with this article?

setIntervalより正確な時計を作る

More than 1 year has passed since last update.

setIntervalは手軽ですが、それが故に正確な時間を必要とする場合には、あまり役に立たなくなります。

手軽に時計を作ってみる

setInterval(関数, 1000);のように書けば、関数を1秒毎に実行することとなります。ここで描画処理を入れれば、秒単位で表示する時計の一丁上がりです。

…が、このままではうまくいかない箇所があります。

  • スタートのタイミングの問題…setIntervalをスタートする瞬間がちょうど秒の変わる瞬間だったという幸運なパターンもありえますが、最悪の場合1秒近く遅れることとなります。
  • 1秒より間隔が長くなる…setIntervalの時間は、「前の実行が終わってから次の実行が始まるまでに、最低限確保される待ち時間」ですので、実行同士の間隔は関数自体の実行時間が足し込まれて、必ずセットした時間を上回ることになります。また、他のコードの実行で遅れることもありえますし、ブラウザ側で意図的に(消費電力節約のためなどで)それ以上待たせてもいいことになっています(W3C HTML 5.2)。
    • 上の2つが重なれば、「遅れがそこまででもないのに秒が飛ぶ」ことも考えられます。1回めを0.999秒で実行開始したとして、タイマーが2ミリ秒余分にかかれば、次の実行は2.001秒となって、1秒台の実行がなくなってしまいます1

このような事情がありますので、「できるだけ安定したサイクルを保ちたい」あるいは「秒が変わった瞬間に実行したい」という場合、setIntervalはあまり適当な選択肢ではありません。

setTimeoutを使う

では、代わりにsetTimeoutを使ってみましょう。setTimeoutでループ処理をさせるときの基本形は、以下のような形です。タイマーで呼ばれた関数から、さらに同じ関数をsetTimeoutする、という流れです。

setTimeout(function main(){
  // メインの処理

  setTimeout(main, 時間);
}, 時間);

あとは時間をどう設定するかだけですが、Date.now()で現在のミリ秒が得られます。「ちょうど秒が変わる瞬間」はシリアル値が1000で割り切れますので、1000 - Date.now() % 1000のようにすれば、そこまでの待ち時間を導けます。

毎回待ち時間を正しく設定することで、秒の変わり目にできるだけ近いところでコンスタントに関数を実行できますし、処理の遅延が1秒以上にならない限り、秒が飛ぶことも起きません。

Codepen上に作ってみました

リロードなどでタイミングを調節すれば、setIntervalのほうが遅れている様子もわかるかと思います。

(なぜかReactで作ってしまったので、ちょっと見づらいかもしれません)

See the Pen pYGKgR by Jkr2255 (@jkr2255) on CodePen.


  1. もちろん、ブラウザ処理の重さなどで、実行が遅れうることは予め想定して実装が必要なのは間違いないです。 

jkr_2255
qiitadon
Qiitadon(β)から生まれた Qiita ユーザー・コミュニティです。
https://qiitadon.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした