83
64

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が即実行されてハマった

Last updated at Posted at 2018-07-30

sleep処理を実装するため、setTimeoutで再帰っぽく書いてたらハマったのでメモ

一見すると問題ないがダメなコード

次のコードは問題ないように見える。
出力もされ、期待通り動いているようだが?

var hoge = function(str) {
  console.log(str)
}

//ダメな書き方
setTimeout(hoge('すぐ実行されてしまう'),4000)

しかし、hoge()がすぐに実行されている事に気づく

こうすると良く分かる。

var hoge = function(str) {
  console.log(str)
}

setTimeout(hoge(''),3000)
setTimeout(hoge(''),2000)
setTimeout(hoge(''),1000)

「あ い う」 という順番で出て欲しいが、
「う い あ」 で出てしまう

ちゃんと動く3つの方法

var hoge = function(str) {
  console.log(str)
}


//良い
setTimeout(hoge,2000,"第三引数に入れてあげる")
setTimeout("hoge('これも可能だが非推奨だから注意')",2000)
setTimeout(function(){console.log('ぐぐると一番良く見る奴')},2000)

順番に解説していく

1 第三引数を使う

setTimeoutの第三引数を使う事で、中の関数に引数を渡すことが出来る

setTimeout(hoge,2000,"あああ")

引数を2つ渡したい場合は第三引数、第四引数 … とする。
hoge() ではなく hogeな点に注意。

2 文字列として渡す

なんと、こんな方法でも動く。

setTimeout("hoge('あああ')",2000)

これが動くのは不思議だが。
https://developer.mozilla.org/ja/docs/Web/API/WindowTimers/setTimeout
を見ると

関数の代わりに文字列を含める代替構文も許容されており、タイマーが満了したときに文字列をコンパイルして実行します。
eval() の使用にリスクがあるのと同じ理由で、この構文は 推奨しません。

setTimeoutはevalみたいに文字列をコードとして解釈するらしい。
非推奨と書いてあるので、あんまりおすすめはしない

3 無名関数を使う

ググると一番よく出てくる方法

setTimeout(function(){console.log('あああ')},2000)

(無名関数 という名称についてはいろいろありますが一般的にそう呼ばれてるのでその話はここではしません)

なんで最初のじゃ動かないのか

setTimeout(hoge('すぐ実行されてしまう'),4000)

では、hogeがまず評価(実行)され、setTimeoutはその結果をwait後に実行する。
hoge('すぐ実行されてしまう')の結果はundefinedのため、

setTimeout(undefined,4000)

を実行している事になる。
だから何も起こらない。

良く考えれば当たり前だが、とても嵌る箇所だと思う。
文法的には間違っていないので厳格モードでも動くし、
waitがかかってないだけで一見動いてはいるのでぼーっとしていると気づかない。

最初みたいな感じで書きたい

//こういう風にもかける
var hoge2 = function(str) {
  return function(){console.log(str)}
}
var hoge3 = str => {
  return () => console.log(str)
}
setTimeout(hoge2('従来のjs'),2000)
setTimeout(hoge3('ES6'),2000)

waitを入れつつ、同じ関数を10回実行する

ダメ

function counter() {
  var count = 0;
  return function re () {
    if (count < 10 ) {
      count++
      console.log(count)
      setTimeout(re(),100)
    }
  }
}
var count = counter();

setTimeout(count(),100)

良い

function counter() {
  var count = 0;
  return function re () {
    if (count < 10 ) {
      count++
      console.log(count)
      setTimeout(re,100)
    }
  }
}
var count = counter();

setTimeout(count,100)

ES6からはPromiseが使えるため、普段なかなか利用しないsetTimeoutであるが
すごくハマった

83
64
3

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
83
64

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?