JavaScriptの setTimeout関数の実行タイミングに関する誤解
JavaScriptの「setTimeout」は、(ミリ秒で)指定した時間後に、指定した関数を実行してくれるのですが、実は実行タイミングがややこしい!のです。私も誤解していました。
例えば、下記を実行すると、どうなるでしょうか?
document.write("hoge\n"); setTimeout(function(){ document.write("fuga\n") }, 1000); document.write("piyo\n");
実行結果は、下記の引用の通り、setTimeout関数で指定した「fuga」という文字列が最後に表示されます。
普通に JavaScript を使いこなしてる人なら、hoge → piyo と表示して、 1 秒後に fuga が表示されるな。って思うはずなんです。
JavaScript を学ぶ際に一番重要なのに、誤解されがちな setTimeout 系の概念 - IT戦記
でも、 JavaScript を始めたばっかりの人の中には、 hoge と表示したあと 1 秒後に fuga → piyo と表示するな。って思ってる人が非常に多い。(経験的に)
タイマーのカウントダウンは「一連の処理が終わってから」
つまり、setTimeoutで指定した時間がカウントダウンされるのは、「一連の処理が終わってから」であり、「setTimeout の行を通過する時ではない」ということです。この「一連の処理」について確認するために、以下のコードを実行してみます。
function timer(){ alert("aaa"); setTimeout( function() {alert("bbb");}, 5000); alert("ccc"); } function main(){ alert("ddd"); timer(); alert("eee"); } main();
実行結果は下記のようになり、特に「eee が表示されてから、5秒後に bbb が表示」されることがポイントです。ここでは、「一連の処理」=「mainメソッド⇒timerメソッド」となり、その一連の処理が完了してから指定時間(ここでは5秒)をカウントしていることが分かります。
ddd aaa ccc eee 〜〜〜 ここで5秒間の待ちが発生 〜〜〜 bbb
この(実行タイミングの)特徴により、以下のことが保証されることになります。
「setTimeoutで指定した関数が実行される時には、他の処理は完了している」
補足1: ノンブロッキング
setTimeoutはノンブロッキング*1であるため、setTimeoutが実行された後、JavaScriptは結果を待たずに次の処理に取りかかることができます。
補足2: 極端に短い時間は指定できない
以下の引用にもあるとおり、あまりに短い時間(1msなど)で実行できてしまうと、クライアントPCに多大な負荷がかかってしまうため、ブラウザ側で最低ラインが設けられているようです。
マルチタスク・マルチウィンドウ・マルチタブ前提のパソコン用のブラウザーの場合、もしJavascriptを全力で実行してしまうと、このベンチマークのように最小の遅延でスクリプトを実行するページを開いたとたんにブラウザー自身のレスポンスが極端に悪くなってしまう。
Life is beautiful: Javascript雑学:SetTimeoutについて知っておくべき事
そのために、たとえプログラマーがJavascript側でSetTimeout()の遅延パラメータとして1msを指定したとしても、最低でも10msとか15.6msの遅延後にしかタイマー関数を呼ばない、という設計になっているのが普通だ