WindowOrWorkerGlobalScope
ミックスインの setTimeout()
メソッド (および window.setTimeout の後継) は、指定された遅延の後に関数またはコードの断片を実行するタイマーを設定します。
構文
var timeoutID = scope.setTimeout(function[, delay, param1, param2, ...]); var timeoutID = scope.setTimeout(function[, delay]); var timeoutID = scope.setTimeout(code[, delay]);
引数
- function
- タイマーが満了した後に実行したい
関数
。 code
- 関数の代わりに文字列を含める構文も許容されており、タイマーが満了したときに文字列をコンパイルして実行します。
eval()
の使用にリスクがあるのと同じ理由で、この構文は 推奨しません。 delay
Optional- 指定した関数やコードを実行する前に待つタイマーの時間をミリ秒 (1/1000 秒) 単位で指定します。この引数を省略すると値 0 を使用しますので "直ちに" 実行する、より正確に言えばできるだけ早く実行することを意味します。どちらの場合も、実際の遅延が想定より長くなることがあります。後述する 遅延が指定値より長い理由 をご覧ください。
param1, ..., paramN
Optional- タイマーが満了したときに、func で指定した関数に渡す追加の引数です。
注記: Internet Explorer 9 およびそれ以前のバージョンでは、最初の構文で関数に渡す追加の引数は動作しないことに注意してください。同様の機能を実現させるには、ポリフィルを使用してください。(ポリフィル を参照)。
戻り値
戻り値 timeoutID
は、setTimeout()
を呼び出して作成したタイマーを識別する正の整数値です。この値は、タイムアウトをキャンセルするために clearTimeout()
へ渡すことができます。
setTimeout()
と setInterval()
は同じ ID プールを共有しており、さらに clearTimeout()
と clearInterval()
は技術的に入れ替えて使用できることを意識すると役に立つでしょう。ただし明快さのために、コードを整備するときは混乱を避けるため、常に一致させるようにするべきです。
同じオブジェクト (window または worker) では、後に setTimeout()
や setInterval()
を呼び出しても timeout ID を再使用しないことが保証されています。ただし、異なるオブジェクトでは別の ID プールを使用します。
例
以下の例はウェブページに 2 つのシンプルなボタンを置いており、setTimeout()
および clearTimeout()
のルーチンを実行します。1 番目のボタンを押下すると 2 秒後にアラートダイアログを呼び出すタイムアウトを設定して、clearTimeout()
で使用するタイムアウト ID を保存します。2 番目のボタンを押下すると、このタイムアウトをキャンセルできます。
HTML コンテンツ
<p>Live Example</p> <button onclick="delayedAlert();">2秒後にアラートボックスを表示する</button> <p></p> <button onclick="clearAlert();">アラートを事前にキャンセルする</button>
JavaScript コンテンツ
var timeoutID; function delayedAlert() { timeoutID = window.setTimeout(slowAlert, 2000); } function slowAlert() { alert('それは実に遅かった!'); } function clearAlert() { window.clearTimeout(timeoutID); }
結果
clearTimeout()
の例 もご覧ください。
ポリフィル
コールバック関数に 1 つ以上の引数を渡す必要があるが、setTimeout()
や setInterval()
を使用して追加の引数を渡す機能をサポートしないブラウザー (例えば Internet Explorer 9 およびそれ以前) で動作しなければならない場合は、HTML5 標準の引数渡し機能を可能にする以下のポリフィルを追加するとよいでしょう。このコードをスクリプトの先頭に追加してください:
/*\ |*| |*| Polyfill which enables the passage of arbitrary arguments to the |*| callback functions of JavaScript timers (HTML5 standard syntax). |*| |*| https://developer.mozilla.org/en-US/docs/DOM/window.setInterval |*| |*| Syntax: |*| var timeoutID = window.setTimeout(func, delay[, param1, param2, ...]); |*| var timeoutID = window.setTimeout(code, delay); |*| var intervalID = window.setInterval(func, delay[, param1, param2, ...]); |*| var intervalID = window.setInterval(code, delay); |*| \*/ (function() { setTimeout(function(arg1) { if (arg1 === 'test') { // feature test is passed, no need for polyfill return; } var __nativeST__ = window.setTimeout; window.setTimeout = function(vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */ ) { var aArgs = Array.prototype.slice.call(arguments, 2); return __nativeST__(vCallback instanceof Function ? function() { vCallback.apply(null, aArgs); } : vCallback, nDelay); }; }, 0, 'test'); var interval = setInterval(function(arg1) { clearInterval(interval); if (arg1 === 'test') { // feature test is passed, no need for polyfill return; } var __nativeSI__ = window.setInterval; window.setInterval = function(vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */ ) { var aArgs = Array.prototype.slice.call(arguments, 2); return __nativeSI__(vCallback instanceof Function ? function() { vCallback.apply(null, aArgs); } : vCallback, nDelay); }; }, 0, 'test'); }())
IE 限定の fix
IE 9 およびそれ以前を含む、他のすべてのモバイルブラウザーやデスクトップブラウザーで完全に目立たないハックが必要である場合は、以下の JavaScript 条件付きコメントを使用できます:
/*@cc_on // conditional IE < 9 only fix @if (@_jscript_version <= 9) (function(f){ window.setTimeout = f(window.setTimeout); window.setInterval = f(window.setInterval); })(function(f){return function(c,t){vara=[].slice.call(arguments,2);return f(function(){c instanceof Function?c.apply(this,a):eval(c)},t)}}); @end @*/
あるいは IE の HTML 条件機能による、とてもクリーンな方法を使用します:
<!--[if lte IE 9]><script> (function(f){ window.setTimeout=f(window.setTimeout); window.setInterval=f(window.setInterval); })(function(f){return function(c,t){ var a=[].slice.call(arguments,2);return f(function(){c instanceof Function?c.apply(this,a):eval(c)},t)} }); </script><![endif]-->
回避策
もうひとつの方法は、コールバックに無名関数を使用することです。ただし、この方法は少し多くコストがかかります。例:
var intervalID = setTimeout(function() { myFunc('one', 'two', 'three'); }, 1000);
上記の例は、アロー関数 を使用して以下のように記述できます:
var intervalID = setTimeout(() => { myFunc('one', 'two', 'three'); }, 1000);
さらに、関数の bind
を使用する方法もあります。例:
setTimeout(function(arg1){}.bind(undefined, 10), 1000);
"this
" 問題
setTimeout()
にメソッド (そういうことならほかの関数も) を渡すとき、コードが実行される際の this
の値が想定とは異なるかもしれません。この問題は JavaScript リファレンス でより詳細に説明されています。
説明
setTimeout()
によって実行されるコードは、setTimeout
が呼び出された関数とは別の実行コンテキスト内で実行されます。呼び出された関数に this
キーワードを設定する通常の規則を適用して、呼び出しあるいは bind
で this
を設定しなければ、非 strict モードでは global
(または window
)、strict モードでは undefined になります。これは、setTimeout
が呼び出された関数の this
値と同じにはなりません。以下の例をご覧ください:
myArray = ['zero', 'one', 'two']; myArray.myMethod = function (sProperty) { alert(arguments.length > 0 ? this[sProperty] : this); }; myArray.myMethod(); // prints "zero,one,two" myArray.myMethod(1); // prints "one"
myMethod
を呼び出したときに、呼び出しによって this
が myArray
に設定されますので、関数内で this[sProperty]
は myArray[sProperty]
と等価です。しかし、以下のコードでは動作が異なります:
setTimeout(myArray.myMethod, 1000); // prints "[object Window]" after 1 second setTimeout(myArray.myMethod, 1500, '1'); // prints "undefined" after 1.5 seconds
myArray.myMethod
関数を setTimeout
に渡しており、関数が呼び出されると this
が前のように設定されず、既定値の window
オブジェクトになります。Array の forEach や reduce などのメソッドにあるような、thisArg
を setTimeout に渡すオプションもありません。また以下のように、this
を設定するために call
を使用する方法も動作しません。
setTimeout.call(myArray, myArray.myMethod, 2000); // error: "NS_ERROR_XPC_BAD_OP_ON_WN_PROTO: Illegal operation on WrappedNative prototype object" setTimeout.call(myArray, myArray.myMethod, 2500, 2); // same error
考えられる解決策
この問題の一般的な解決策は、this
に必要な値を設定するラッパー関数を使用することです:
setTimeout(function(){myArray.myMethod()}, 2000); // prints "zero,one,two" after 2 seconds setTimeout(function(){myArray.myMethod('1')}, 2500); // prints "one" after 2.5 seconds
代わりにアロー関数も使用できます:
setTimeout(() => {myArray.myMethod()}, 2000); // prints "zero,one,two" after 2 seconds setTimeout(() => {myArray.myMethod('1')}, 2500); // prints "one" after 2.5 seconds
他に考えられる "this
" 問題の解決策として、本来の setTimeout()
および setInterval()
グローバル関数を、this
オブジェクトを渡せるようにして、コールバックで Function.prototype.call
を使用して設定するように置き換える方法があります。例えば:
// Enable setting 'this' in JavaScript timers var __nativeST__ = window.setTimeout, __nativeSI__ = window.setInterval; window.setTimeout = function (vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */) { var oThis = this, aArgs = Array.prototype.slice.call(arguments, 2); return __nativeST__(vCallback instanceof Function ? function () { vCallback.apply(oThis, aArgs); } : vCallback, nDelay); }; window.setInterval = function (vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */) { var oThis = this, aArgs = Array.prototype.slice.call(arguments, 2); return __nativeSI__(vCallback instanceof Function ? function () { vCallback.apply(oThis, aArgs); } : vCallback, nDelay); };
新機能のテスト:
myArray = ['zero', 'one', 'two']; myArray.myMethod = function (sProperty) { alert(arguments.length > 0 ? this[sProperty] : this); }; setTimeout(alert, 1500, 'Hello world!'); // the standard use of setTimeout and setInterval is preserved, but... setTimeout.call(myArray, myArray.myMethod, 2000); // prints "zero,one,two" after 2 seconds setTimeout.call(myArray, myArray.myMethod, 2500, 2); // prints "two" after 2.5 seconds
this
の値を設定する Function.prototype.bind()
メソッドを導入しました。これにより、コールバックで this
の値を設定するためにラッパー関数を使用しなければならない状況を回避できます。bind()
の使用例:
myArray = ['zero', 'one', 'two']; myBoundMethod = (function (sProperty) { console.log(arguments.length > 0 ? this[sProperty] : this); }).bind(myArray); myBoundMethod(); // prints "zero,one,two" because 'this' is bound to myArray in the function myBoundMethod(1); // prints "one" setTimeout(myBoundMethod, 1000); // still prints "zero,one,two" after 1 second because of the binding setTimeout(myBoundMethod, 1500, "1"); // prints "one" after 1.5 seconds
注記
clearTimeout()
を使ってタイムアウトを中止することができます。もし関数を繰返し (すなわち、N ミリ秒ごとに) 呼びたいなら、setInterval()
を使うことを考慮してください。
文字列リテラルの使用
関数の代わりに、文字列を setTimeout()
に渡すと、eval を使うのと同様の危険性があります。
// 推奨 window.setTimeout(function() { alert('Hello World!'); }, 500); // 非推奨 window.setTimeout("alert('Hello World!');", 500);
setTimeout
に渡した文字列はグローバルコンテキストで評価されます。そのため、setTimeout()
が呼び出されたコンテキストのローカルシンボルは、文字列を評価したコードからは利用できません。
遅延が指定値より長い理由
タイムアウトが満了するまでに予想より長い時間がかかる理由は複数あります。このセクションでは、もっとも一般的な理由を説明します。
ネストされたタイムアウトを 4ms 以上に制限する
歴史的に ブラウザー setTimeout()
に制限を課しています。「最小遅延」の制限より短い遅延で呼び出される、連続の setTimeout()
は、少なくとも最小遅延を使用することになります。その最小遅延、DOM_MIN_TIMEOUT_VALUE
は 4 ms (Firefox の dom.min_timeout_value
の設定に保存されています) であり、DOM_CLAMP_TIMEOUT_NESTING_LEVEL
は 5 です。
実際、4 ms は HTML5 の仕様で標準化されています。 そして、2010 年以降にリリースされたブラウザー間で一貫しています。(Firefox 5.0 / Thunderbird 5.0 / SeaMonkey 2.2) 以前では、ネストされた setTimeout の最小値は 10 ms でした。
0 ms タイマーをモダンブラウザーで実装するには、ここで説明されている window.postMessage()
を利用できます。
非アクティブタブのタイムアウトを 1000 ms 以上に制限する
バックグラウンドのタブによる負荷 (およびバッテリー消費) を軽減するため、アクティブ状態でないタブでのタイマーの呼び出しは、1 秒 (1000 ms) あたり 1 回までとなります。
Firefox はこの動作をバージョン 5 (バグ 633421 を参照。1000 ms の定数は設定項目 dom.min_background_timeout_value
で変更できます) から、Chrome はこの動作をバージョン 11 (crbug.com/66078) から実装しています。
Android 版 Firefox は バグ 736602 によって、Firefox 14 からバックグラウンドタブで 15 分のタイムアウト値を使用しており、またバックグラウンドタブを完全にアンロードすることもできます。
Firefox 50 では、Web Audio API の AudioContext
が音声を再生中であればバックグラウンドタブの制限を行いません。さらに Firefox 51 では、音声を再生していなくても AudioContext
を提供していれば、バックグラウンドタブの制限を行わないように改良しました。これによりタブがバックグラウンドであるときに、楽譜を基に音楽を再生するアプリで拍子が合わない、あるいは音楽が正しく同期しないといった問題が解決します。
トラッキングスクリプトのタイムアウトを制限する
Firefox 55 より、トラッキングスクリプト (例えば Google Analytics や、TP リスト によって Firefox がトラッキングスクリプトであると認識するスクリプトの URL) にさらなる制限を課します。フォアグラウンドで実行しているとき、最小遅延の制限は 4ms のままです。しかしバックグラウンドのタブでは、最小遅延を 10000ms または 10 秒に制限します。これはドキュメントが最初に読み込まれてから 30 秒後に発効します。
この動作を制御する設定項目は以下のとおりです:
dom.min_tracking_timeout_value
: 4dom.min_tracking_background_timeout_value
: 10000dom.timeout.tracking_throttling_delay
: 30000
タイムアウトの遅延
前出の "制限" に加えて、ページ内 (またはOSやブラウザー自身) の他のタスクの処理に時間がかかると、タイムアウトは遅れます。注目すべき重要なケースとして、setTimeout()
を呼び出したスレッドが終了するまで関数やコードスニペットを実行できないことが挙げられます。例えば:
function foo() { console.log('foo has been called'); } setTimeout(foo, 0); console.log('After setTimeout');
このコードは、コンソールへ以下のように出力します:
After setTimeout foo has been called
これは setTimeout
を遅延 0 で呼び出したとしても、直ちに実行するのではなくキューに載せて、次の機会に実行するようスケジューリングされるためです。現在実行中のコードはキューにある関数を実行する前に完了しなければならず、このために実行結果の順序が想定どおりにならない場合があります。
最大遅延時間
Internet Explorer、Chrome、Safari、Firefox を含むブラウザーは、内部で遅延時間を 32 ビット符号付き整数値で保存します。このため 2147483647 (約 24.8 日) より大きな遅延時間を使用すると整数値がオーバーフローして、その結果直ちに実行されるタイムアウトになります。
仕様
仕様書 | 策定状況 | コメント |
---|---|---|
HTML Living Standard WindowOrWorkerGlobalScope.setTimeout() の定義 |
現行の標準 | 最新の仕様で、メソッドを WindowOrWorkerGlobalScope ミックスインに移動。 |
HTML Living Standard WindowTimers.setTimeout() の定義 |
現行の標準 | 最初期の定義 (DOM Level 0) |
ブラウザー実装状況
We're converting our compatibility data into a machine-readable JSON format. This compatibility table still uses the old format, because we haven't yet converted the data it contains. Find out how you can help!
機能 | Chrome | Edge | Firefox (Gecko) | Internet Explorer | Opera | Safari |
---|---|---|---|---|---|---|
基本サポート | 1.0 | (有) | 1.0 (1.7 or earlier) 52 (52)[2] |
4.0 | 4.0 | 1.0 |
コールバック関数[1] の引数のサポート | (有) | (有) | (有) | 10.0 | (有) | (有) |
トラッキングスクリプトのタイムアウトを制限[3] | ? | ? | 55 (55) | ? | ? | ? |
機能 | Android | Android 版 Chrome | Edge | Firefox Mobile (Gecko) | IE Mobile | Opera Mobile | Safari Mobile |
---|---|---|---|---|---|---|---|
基本サポート | 1.0 | 1.0 | (有) | 1.0 (1) 52.0 (52)[2] |
6.0 | 6.0 | 1.0 |
コールバック関数[1] の引数のサポート | ? | ? | ? | ? | ? | ? | ? |
トラッキングスクリプトのタイムアウトを制限[3] | ? | ? | ? | 55.0 (55) | ? | ? | ? |
[1] 最初の形式で省略可能な引数をサポートするか否か。
[2] setTimeout()
を WindowOrWorkerGlobalScope
ミックスインで定義しています。
[3] トラッキングスクリプトのタイムアウトを制限する をご覧ください。