15
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

JavaScriptで長めのsetTimeoutを使う時はOSのスリープを考慮する

Posted at

今までsetTimeoutの遅延時間は長くても数十秒程度までしか指定したことがありませんでした。
先日、長時間のタイマー処理がうまく動作しないケースがあったのでご紹介させていただきます。

初めに書いたコード

「15分後に実行する」という処理をする際、普通に考えると以下のように書いてしまいます。

setTimeout(() => {
  /* なにか */
  /* 素敵なことを */
  /* 行なう */
}, 15 * 60 * 1000);

このコードはうまく動きそうですが、実は落とし穴がありました。

スマホもPCも操作しないで放置すると、省電力のためスリープモードになりますよね。
そう、スリープ中はタイマーが止まる1んです。

なので、タイマーが完了するのは開始から30分後かもしれないし、2時間後や翌朝になってしまう場合もあります。

スリープ対策を入れる

今回はそうなってしまうと困る案件だったので、setIntervalで「開始から15分経ったか」をチェックし続ける処理に変更しました。

const targetTime = Date.now() + (15 * 60 * 1000);

const timerId = setInterval(() => {
  if (Date.now() > targetTime) {
    clearInterval(timerId);
    /* なにか */
    /* 素敵なことを */
    /* 行なう */
  }
}, 500);

これなら途中でスリープしていた場合でも開始から15分後に処理が走るし、スリープ中に15分を過ぎていたら、復帰した瞬間に実行されるようになります。
※あまり頻繁だとUIなど他の処理に影響が出るかと思い、500ms間隔での実行にしています。どれくらいの誤差を許容できるかによって設定を変えてください。

おわりに

最初のコードを書いた後、他の人にテストしてもらった結果「時間になっても何も起きないです」と言われたものの、自分の環境では再現しなかった2ので、原因が分かる時間がかかりました……
「気づいてみたら単純なことだった」っていうことは結構ありますよね。

  1. この現象を紹介しているサイトはいくつかありましたが、ブラウザや言語の仕様的なページ見つけられませんでした。

  2. PCとスマホ両方テストしましたが、PCは他の作業をしながらだったのでスリープしないし、スマホは何となく(スリープしないように)定期的にタップしてました。何度もテストを繰り返すうちにたまたまスリープしてしまい、時間通りに処理が走らなかったことが真相解明のきっかけになりました。

15
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?