From 7f350142b5c4ba853f825e184c5fae89fb7d1905 Mon Sep 17 00:00:00 2001 From: Masahiro FUJIMOTO Date: Thu, 10 Mar 2022 01:42:31 +0900 Subject: 2022/02/04 時点の英語版に同期 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- files/ja/web/api/settimeout/index.md | 552 +++++++++++++++-------------------- 1 file changed, 234 insertions(+), 318 deletions(-) diff --git a/files/ja/web/api/settimeout/index.md b/files/ja/web/api/settimeout/index.md index 865c4679a6..eb70ee7167 100644 --- a/files/ja/web/api/settimeout/index.md +++ b/files/ja/web/api/settimeout/index.md @@ -1,420 +1,336 @@ --- -title: WindowOrWorkerGlobalScope.setTimeout() +title: setTimeout() slug: Web/API/setTimeout tags: - API - HTML DOM - Intervals - - JavaScript timers + - JavaScript タイマー - MakeBrowserAgnostic - - Method - - NeedsCompatTable + - メソッド - NeedsMarkupWork - - NeedsUpdate - - Reference + - リファレンス - Timers - - WindowOrWorkerGlobalScope - setTimeout + - ポリフィル +browser-compat: api.setTimeout translation_of: Web/API/WindowOrWorkerGlobalScope/setTimeout original_slug: Web/API/WindowOrWorkerGlobalScope/setTimeout --- -
{{APIRef("HTML DOM")}}
+{{APIRef("HTML DOM")}} -

setTimeout() は {{domxref("WindowOrWorkerGlobalScope")}} ミックスインのメソッド (および Window.setTimeout() の後継) で、時間切れになると関数または指定されたコードの断片を実行するタイマーを設定します。

+グローバルの **`setTimeout()`** メソッドは、時間切れになると関数または指定されたコードの断片を実行するタイマーを設定します。 -

構文

+## 構文 -
var timeoutID = scope.setTimeout(function[, delay, arg1, arg2, ...]);
-var timeoutID = scope.setTimeout(function[, delay]);
-var timeoutID = scope.setTimeout(code[, delay]);
-
+```js +var timeoutID = setTimeout(function[, delay, arg1, arg2, ...]); +var timeoutID = setTimeout(function[, delay]); +var timeoutID = setTimeout(code[, delay]); +``` -

引数

+### 引数 -
-
function
-
タイマーが満了した後に実行したい {{jsxref("function", "関数")}}。
-
code
-
関数の代わりに文字列を含める代替構文も許容されており、タイマーが満了したときに文字列をコンパイルして実行します。 {{jsxref("Global_Objects/eval", "eval()")}} の使用にリスクがあるのと同じ理由で、この構文は推奨しません
-
delay {{optional_inline}}
-
指定した関数やコードを実行する前に待つタイマーの時間をミリ秒 (1/1000 秒) 単位で指定します。この引数を省略すると値 0 を使用しますので "直ちに" 実行する、より正確に言えばできるだけ早く実行することを意味します。どちらの場合も、実際の遅延が想定より長くなることがあります。後述する {{anch("Reasons for delays longer than specified", "遅延が指定値より長い理由")}} をご覧ください。
-
arg1, ..., argN {{optional_inline}}
-
タイマーが満了したときに、 function で指定された関数に渡す追加の引数です。
-
+- `function` + - : タイマーが満了した後に実行する{{jsxref("function", "関数")}}。 +- `code` + - : 関数の代わりに文字列を含める代替構文も許容されており、タイマーが満了したときに文字列をコンパイルして実行します。 {{jsxref("Global_Objects/eval", "eval()")}} の使用にリスクがあるのと同じ理由で、この構文は**推奨しません**。 +- `delay` {{optional_inline}} + - : 指定した関数やコードを実行する前に待つタイマーの時間をミリ秒 (1/1000 秒) 単位で指定します。この引数を省略すると値 0 を使用しますので "直ちに" 実行する、より正確に言えばできるだけ早く実行することを意味します。どちらの場合も、実際の遅延が想定より長くなることがあります。後述する{{anch("遅延が指定値より長い理由")}}をご覧ください。 +- `arg1, ..., argN` {{optional_inline}} + - : タイマーが満了したときに、 `function` で指定された関数に渡す追加の引数です。 -
-

: Internet Explorer 9 およびそれ以前のバージョンでは、最初の構文で関数に渡す追加の引数は動作しないことに注意してください。同様の機能を実現させるには、ポリフィルを使用してください。({{anch("Polyfill", "ポリフィル")}} を参照)。

-
+## 返値 -

返値

+返される `timeoutID` は正の整数値で、 `setTimeout()` を呼び出して作成したタイマーを識別します。この値を {{domxref("clearTimeout","clearTimeout()")}} へ渡すことで、タイムアウトを取り消すことができます。 -

返される timeoutID は正の整数値で、 setTimeout() を呼び出して作成したタイマーを識別します。この値を {{domxref("WindowOrWorkerGlobalScope.clearTimeout","clearTimeout()")}} へ渡すことで、タイムアウトを取り消すことができます。

+`timeoutID` の値は、同じオブジェクト (ウィンドウやワーカー) において、後に `setTimeout()` や `setInterval()` を呼び出しても再使用されないことが保証されています。ただし、別なオブジェクトでは別の ID プールを使用します。 -

setTimeout() と {{domxref("WindowOrWorkerGlobalScope.setInterval", "setInterval()")}} は同じ ID プールを共有しており、さらに clearTimeout() と {{domxref("WindowOrWorkerGlobalScope.clearInterval", "clearInterval()")}} は技術的に入れ替えて使用できることを意識すると役に立つかもしれません。ただし明確さのために、コードを整備するときは混乱を避けるため、常に一致させるようにするべきです。

+## 解説 -

同じオブジェクト (ウィンドウやワーカー) では、後に setTimeout()setInterval() を呼び出しても タイムアウト ID を再使用しないことが保証されています。ただし、別なオブジェクトでは別の ID プールを使用します。

+タイムアウトは、 {{domxref("clearTimeout()")}} を使用して取り消すことができます。 -

+関数を繰り返して(例えば _N_ ミリ秒ごとに)呼び出すには、 {{domxref("setInterval()")}} を使用することを検討してください。 -

以下の例はウェブページに 2 つのシンプルなボタンを置いており、setTimeout() および clearTimeout() のルーチンを実行します。1 番目のボタンを押下すると 2 秒後にアラートダイアログを呼び出すタイムアウトを設定して、clearTimeout() で使用するタイムアウト ID を保存します。2 番目のボタンを押下すると、このタイムアウトをキャンセルできます。

+### 非同期関数の動作 -

HTML

+`setTimeout()` は非同期関数です。これは、タイマー関数は関数スタック内の他の関数の実行を停止させないということです。 +言い換えると、 `setTimeout()` を使って、関数スタックの次の関数が起動するまでの「間」を作ることはできません。 -
<p>Live Example</p>
-<button onclick="delayedAlert();">2秒後にアラートボックスを表示する</button>
-<p></p>
-<button onclick="clearAlert();">アラートを事前にキャンセルする</button>
-
+以下の例をご覧ください。 -

JavaScript

+```js + setTimeout(() => {console.log("this is the first message")}, 5000); + setTimeout(() => {console.log("this is the second message")}, 3000); + setTimeout(() => {console.log("this is the third message")}, 1000); -
var timeoutID;
+  // 出力:
 
-function delayedAlert() {
-  timeoutID = window.setTimeout(window.alert, 2*1000, '本当に遅い!');
-}
-
-function clearAlert() {
-  window.clearTimeout(timeoutID);
-}
-
- -

結果

- -

{{EmbedLiveSample('Example')}}

- -

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[, arg1, arg2, ...]);
-|*|  var timeoutID = window.setTimeout(code, delay);
-|*|  var intervalID = window.setInterval(func, delay[, arg1, arg2, ...]);
-|*|  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 限定の修正

- -

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){var a=[].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 キーワードを設定する通常の規則を適用して、呼び出しあるいは bindthis を設定しなければ、非 strict モードでは global (または window)、strict モードでは undefined になります。これは、setTimeout が呼び出された関数の this 値と同じにはなりません。

- -
-

注: setTimeout コールバックの既定の this の値は、厳格モードであっても undefined ではなく、 window オブジェクトです。

-
- -

以下の例をご覧ください。

- -
myArray = ['zero', 'one', 'two'];
-myArray.myMethod = function (sProperty) {
-  alert(arguments.length > 0 ? this[sProperty] : this);
-};
+  // this is the third message
+  // this is the second message
+  // this is the first message
+```
 
-myArray.myMethod(); // prints "zero,one,two"
-myArray.myMethod(1); // prints "one"
+最初の関数は、 2 番目の関数を呼び出す前に 5 秒間の「間」を作らないことに注意してください。その代わり、 1 番目の関数が呼び出されますが、実行されるまで 5 秒間待機します。 1 番目の関数が実行を待っている間に 2 番目の関数が呼び出され、 2 番目の関数が実行される前に 3 秒の待ち時間が適用されます。 1 番目の関数も 2 番目の関数もタイマーが終了していないので、 3 番目の関数が呼び出され、先に実行を完了します。その後、 2 番目の関数が続きます。そして、最後に 1 番目の関数のタイマーが終了した後、 1 番目の関数が実行されます。 -

myMethod を呼び出したときに、呼び出しによって thismyArray に設定されますので、関数内で this[sProperty]myArray[sProperty] と等価です。しかし、以下のコードでは動作が異なります。

+ある関数が実行された後に別の関数が実行されるような処理を行うには、[プロミス](/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise)のドキュメントを参照してください。 -
setTimeout(myArray.myMethod, 1.0*1000); // 1秒後に "[object Window]" と表示
-setTimeout(myArray.myMethod, 1.5*1000, '1'); // 1.5秒後に "undefined" と表示
+### "this" の問題 -

myArray.myMethod 関数を setTimeout に渡しており、関数が呼び出されると this が前のように設定されず、既定値の window オブジェクトになります。Array の forEach や reduce などのメソッドにあるような、thisArg を setTimeout に渡すオプションもありません。また以下のように、this を設定するために call を使用する方法も動作しません。

+`setTimeout()` にメソッドを渡すと、 `this` が期待とは異なる値で起動されることがあります。一般的な問題は [JavaScript リファレンス](/ja/docs/Web/JavaScript/Reference/Operators/this#オブジェクトのメソッドとして)で詳細に説明されています。 -
setTimeout.call(myArray, myArray.myMethod, 2.0*1000); // error: "NS_ERROR_XPC_BAD_OP_ON_WN_PROTO: Illegal operation on WrappedNative prototype object"
-setTimeout.call(myArray, myArray.myMethod, 2.5*1000, 2); // same error
-
+`setTimeout()` によって実行されるコードは、 `setTimeout` が呼び出された関数とは別の実行コンテキスト内から呼び出されます。呼び出された関数に `this` キーワードを設定する通常の規則を適用して、呼び出しあるいは `bind` で `this` を設定しなければ、厳格モードでなければ `global` (または `window`)、厳格モードでは undefined になります。これは、 `setTimeout` が呼び出された関数の `this` 値と同じにはなりません。 -

考えられる解決策

+以下の例をご覧ください。 -

この問題の一般的な解決策は、this に必要な値を設定するラッパー関数を使用することです:

+```js +const myArray = ['zero', 'one', 'two']; +myArray.myMethod = function (sProperty) { + console.log(arguments.length > 0 ? this[sProperty] : this); +}; -
setTimeout(function(){myArray.myMethod()}, 2.0*1000); // prints "zero,one,two" after 2 seconds
-setTimeout(function(){myArray.myMethod('1')}, 2.5*1000); // prints "one" after 2.5 seconds
+myArray.myMethod(); // "zero,one,two" と表示 +myArray.myMethod(1); // "one" と表示 +``` -

代わりにアロー関数も使用できます。

+`myMethod` を呼び出したときに、呼び出しによって `this` が `myArray` に設定されますので、関数内で `this[sProperty]` は `myArray[sProperty]` と等価です。しかし、以下のコードでは動作が異なります。 -
setTimeout(() => {myArray.myMethod()}, 2.0*1000); // prints "zero,one,two" after 2 seconds
-setTimeout(() => {myArray.myMethod('1')}, 2.5*1000); // prints "one" after 2.5 seconds
+```js +setTimeout(myArray.myMethod, 1.0*1000); // "[object Window]" と 1 秒後に表示 +setTimeout(myArray.myMethod, 1.5*1000, '1'); // "undefined" と 1.5 秒後に表示 +``` -

他に考えられる "this" 問題の解決策として、本来の setTimeout() および setInterval() グローバル関数を、this オブジェクトを渡せるようにして、コールバックで Function.prototype.call を使用して設定するように置き換える方法があります。例えば:

+`myArray.myMethod` 関数を `setTimeout` に渡しており、関数が呼び出されると `this` が前のように設定されず、既定の `window` オブジェクトになります。 -
// Enable setting 'this' in JavaScript timers
+Array の {{jsxref("Array.forEach()", "forEach()")}} や {{jsxref("Array.reduce()", "reduce()")}} などのメソッドにあるような、`thisArg` を `setTimeout` に渡すオプションもありません。また以下のように、`this` を設定するために `call` を使用する方法も動作しません。
 
-var __nativeST__ = window.setTimeout,
-    __nativeSI__ = window.setInterval;
+```js
+setTimeout.call(myArray, myArray.myMethod, 2.0*1000); // エラー
+setTimeout.call(myArray, myArray.myMethod, 2.5*1000, 2); // 同じエラー
+```
 
-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);
-};
+##### ラッパー関数の使用 -
: これら 2 つの置き換えにより、IE のタイマーで HTML5 標準の、コールバック関数に任意の引数を渡すことも可能になります。よって、ポリフィルとしても使用できます。Callback arguments の節をご覧ください。
+この問題の一般的な解決策は、`this` に必要な値を設定するラッパー関数を使用することです。 -

新機能のテスト:

+```js +setTimeout(function(){myArray.myMethod()}, 2.0*1000); // "zero,one,two" と 2 秒後に表示 +setTimeout(function(){myArray.myMethod('1')}, 2.5*1000); // "one" と 2.5 秒後に表示 +``` -
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, 2.0*1000); // prints "zero,one,two" after 2 seconds
-setTimeout.call(myArray, myArray.myMethod, 2.5*1000, 2); // prints "two" after 2.5 seconds
-
+```js +setTimeout(() => {myArray.myMethod()}, 2.0*1000); // "zero,one,two" と 2 秒後に表示 +setTimeout(() => {myArray.myMethod('1')}, 2.5*1000); // "one" と 2.5 秒後に表示 +``` -
: JavaScript 1.8.5 で、関数のすべての呼び出しに対して this の値を設定する Function.prototype.bind() メソッドを導入しました。これにより、コールバックで this の値を設定するためにラッパー関数を使用しなければならない状況を回避できます。
+##### bind() の使用 -

bind() の使用例:

+他に、 {{jsxref("Function.bind()", "bind()")}} を使用して `this` の値をその関数のすべての呼び出しに設定することができます。 -
myArray = ['zero', 'one', 'two'];
-myBoundMethod = (function (sProperty) {
-    console.log(arguments.length > 0 ? this[sProperty] : this);
+```js
+const myArray = ['zero', 'one', 'two'];
+const 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, 1.0*1000); // still prints "zero,one,two" after 1 second because of the binding
-setTimeout(myBoundMethod, 1.5*1000, "1"); // prints "one" after 1.5 seconds
-
+myBoundMethod(); // "zero,one,two" と表示。関数内で 'this' が myArray に結び付けられているため。 +myBoundMethod(1); // "one" と表示 +setTimeout(myBoundMethod, 1.0*1000); // こちらも結びつけがあるため "zero,one,two" と 1 秒後に表示 +setTimeout(myBoundMethod, 1.5*1000, "1"); // "one" と 1.5 秒後に表示 +``` -

+### 文字列リテラルの使用 -

タイムアウトは {{domxref("WindowOrWorkerGlobalScope.clearTimeout","clearTimeout()")}} を使って中止することができます。

+関数の代わりに文字列を `setTimeout()` に渡すと、[`eval()`](/ja/docs/Web/JavaScript/Reference/Global_Objects/eval) を使うのと同様の問題が発生します。 -

もし関数を繰返し (例えば、 N ミリ秒ごとに) 呼び出したいなら、 {{domxref("WindowOrWorkerGlobalScope.setInterval()","setInterval()")}} を使うことを検討してください。

+```js example-bad +// こうやってはいけない +setTimeout("console.log('Hello World!');", 500); +``` -

文字列リテラルの使用

+```js example-good +// こうすればよい +setTimeout(function() { + console.log('Hello World!'); +}, 500); +``` -

関数の代わりに文字列を setTimeout() に渡すと、eval を使うのと同様の問題が発生します。

+`{{domxref("setTimeout()")}}` に渡した文字列はグローバルコンテキストで評価されます。そのため、`{{domxref("setTimeout()")}}` が呼び出されたコンテキストのローカルシンボルは、文字列を評価したコードからは利用できません。 -
// 推奨
-window.setTimeout(function() {
-  alert('Hello World!');
-}, 500);
+### 遅延が指定値より長い理由
 
-// 非推奨
-window.setTimeout("alert('Hello World!');", 500);
-
+タイムアウトが満了するまでに予想より長い時間がかかる理由は複数あります。この節では、もっとも一般的な理由を説明します。 -

setTimeout に渡した文字列はグローバルコンテキストで評価されます。そのため、setTimeout() が呼び出されたコンテキストのローカルシンボルは、文字列を評価したコードからは利用できません。

+#### 入れ子のタイムアウト -

遅延が指定値より長い理由

+[HTML 標準](https://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html#timers)で指定されているとおり、ブラウザーは `setTimeout` の入れ子になった呼び出しが5回スケジュールされると、最小 4 ミリ秒のタイムアウトを強制します。 -

タイムアウトが満了するまでに予想より長い時間がかかる理由は複数あります。このセクションでは、もっとも一般的な理由を説明します。

+この例では、 `setTimeout` の呼び出しを `0` ミリ秒の遅延でネストし、ハンドラーが呼び出されるたびに遅延時間を記録しています。最初の 4 回は遅延が約 0 ミリ秒、その後は約 4 ミリ秒になります。 -

タイムアウトを 4ms 以上に制限する

+```html + +
previous    this    actual delay
+
+``` -

現代のブラウザーは、setTimeout() や {{domxref("WindowOrworkerGlobalScope.setInterval","setInterval()")}} がコールバックのネスト (ネストの深さが少なくとも数段階ある) によって連続的に呼び出された、あるいは連続的なインターバルが数回発生した後に呼び出されたときに、少なくとも 4 ミリ秒ごとに呼び出されるように制限をかけます。例えば:

+```js +let last = 0; +let iterations = 10; -
function cb() { f(); setTimeout(cb, 0); }
-setTimeout(cb, 0);
-setInterval(f, 0);
+function timeout() { + // log the time of this call + logline(new Date().getMilliseconds()); -

Chrome および Firefox では、5 回目の連続的なコールバックの呼び出しで制限をかけます。また Safar は 6 回目、Edge は 3 回目で制限をかけます。Gecko は バージョン 56 で、setInterval() で制限を始めました (後述のとおり setTimeout() は以前から行っていました)。

+ // if we are not finished, schedule the next call + if (iterations-- > 0) { + setTimeout(timeout, 0); + } +} -

歴史的に 一部のブラウザー (例えば Firefox) は、あらゆる場所から呼び出された setInterval()、あるいはネストの深さが少なくとも数段階ある setTimeout() が呼び出されたときの制限を、若干異なる動作で実装しています。。

+function run() { + // clear the log + const log = document.querySelector("#log"); + while (log.lastElementChild) { + log.removeChild(log.lastElementChild); + } -

0 ms タイマーをモダンブラウザーで実装するには、ここで説明されている {{domxref("window.postMessage()")}} を利用できます。

+ // initialize iteration count and the starting timestamp + iterations = 10; + last = new Date().getMilliseconds(); -
-

: 最小遅延である DOM_MIN_TIMEOUT_VALUE は 4 ms (Firefox の dom.min_timeout_value の設定に保存されています) であり、DOM_CLAMP_TIMEOUT_NESTING_LEVEL は 5 です。

-
+ // start timer + setTimeout(timeout, 0); +} -
-

: 4 ms は HTML5 の仕様で標準化されています。 そして、2010 年以降にリリースされたブラウザー間で一貫しています。{{geckoRelease("5.0")}} 以前では、ネストされた setTimeout の最小値は 10 ms でした。

-
+function pad(number) { + return number.toString().padStart(3, "0"); +} + +function logline(now) { + // log the last timestamp, the new timestamp, and the difference + const newLine = document.createElement("pre"); + newLine.textContent = `${pad(last)} ${pad(now)} ${now - last}`; + document.getElementById("log").appendChild(newLine); + last = now; +} -

非アクティブタブのタイムアウトは 1000 ms 以上に制限される

+document.querySelector("#run").addEventListener("click", run); +``` -

バックグラウンドのタブによる負荷 (およびバッテリー消費) を軽減するため、アクティブ状態でないタブでのタイマーの呼び出しは、1 秒 (1,000 ms) あたり 1 回までとなります。

+{{EmbedLiveSample("Nested_timeouts", 100, 420)}} -

Firefox はこの動作をバージョン 5 ({{bug(633421)}} を参照。1000 ms の定数は設定項目 dom.min_background_timeout_value で変更できます) から、Chrome はこの動作をバージョン 11 (crbug.com/66078) から実装しています。

+#### アクティブでないタブのタイムアウト -

Android 版 Firefox は {{bug(736602)}} によって、 Firefox 14 からバックグラウンドタブで 15 分のタイムアウト値を使用しており、またバックグラウンドタブを完全にアンロードすることもできます。

+バックグラウンドのタブによる負荷(および関連するバッテリーの使用量)を軽減するために、ブラウザはアクティブでないタブの最小タイムアウト時間を強制します。また、ページがウェブ音声 API の {{domxref("AudioContext")}} を使用して音声を再生している場合、このタイムアウトが免除されることもあります。 -
-

Firefox 50 では、 Web Audio API の {{domxref("AudioContext")}} が音声を再生中であればバックグラウンドタブの制限を行いません。さらに Firefox 51 では、音声を再生していなくても {{domxref("AudioContext")}} を提供していれば、バックグラウンドタブの制限を行わないように改良しました。これによりタブがバックグラウンドであるときに、楽譜を基に音楽を再生するアプリで拍子が合わない、あるいは音楽が正しく同期しないといった問題が解決します。

-
+この仕様はブラウザーに依存します。 -

トラッキングスクリプトのタイムアウトを制限する

+- Firefox のデスクトップ版と Chrome では、アクティブでないタブの最小タイムアウトは 1 秒です。 +- Android 版 Firefox では、非アクティブなタブのタイムアウトは最低 15 分で、タブを完全にアンロードすることもできます。 +- Firefox は、タブに {{domxref("AudioContext")}} が含まれている場合、アクティブでないタブをスロットルで処理しません。 -

Firefox 55 より、トラッキングスクリプト (例えば Google Analytics や、TP リスト によって Firefox がトラッキングスクリプトであると認識するスクリプトの URL) にさらなる制限を課します。フォアグラウンドで実行しているとき、最小遅延の制限は 4ms のままです。しかしバックグラウンドのタブでは、最小遅延を 10,000ms または 10 秒に制限します。これはドキュメントが最初に読み込まれてから 30 秒後に発効します。

+#### トラッキングスクリプトのタイムアウトを制限する -

この動作を制御する設定項目は以下のとおりです。

+Firefox は、トラッキングスクリプトとして認識されたスクリプトに対して追加のスロットルを適用します。 +フォアグラウンドで実行されている場合、最小遅延は 4ms のままです。しかし、バックグラウンドのタブでは、最小遅延時間は 10,000ms (10 秒)で、文書が最初に読み込まれてから 30 秒後に有効になります。 - +詳しくは、[トラッキング保護](https://wiki.mozilla.org/Security/Tracking_protection)を参照してください。 -

タイムアウトの遅延

+#### タイムアウトの遅延 -

前出の "制限" に加えて、ページ内 (またはOSやブラウザー自身) の他のタスクの処理に時間がかかると、タイムアウトは遅れます。注目すべき重要なケースとして、setTimeout() を呼び出したスレッドが終了するまで関数やコードスニペットを実行できないことが挙げられます。例えば:

+ページ(または OS やブラウザー)が他のタスクでビジー状態場合、タイムアウトが予想より遅れて発生することがあります。注意すべき重要なケースとして、 `setTimeout()` を呼び出したスレッドが終了するまで、関数やコードスニペットを実行することができないことがあります。例えば、 -
function foo() {
+```js
+function foo() {
   console.log('foo has been called');
 }
 setTimeout(foo, 0);
-console.log('After setTimeout');
+console.log('After setTimeout'); +``` + +このコードは、コンソールへ以下のように出力します。 -

このコードは、コンソールへ以下のように出力します。

+``` +After setTimeout +foo has been called +``` -
After setTimeout
-foo has been called
+これは `setTimeout` を遅延 0 で呼び出したとしても、直ちに実行するのではなくキューに載せて、次の機会に実行するようスケジューリングされるためです。現在実行中のコードはキューにある関数を実行する前に完了しなければならず、このために実行結果の順序が想定どおりにならない場合があります。 -

これは setTimeout を遅延 0 で呼び出したとしても、直ちに実行するのではなくキューに載せて、次の機会に実行するようスケジューリングされるためです。現在実行中のコードはキューにある関数を実行する前に完了しなければならず、このために実行結果の順序が想定どおりにならない場合があります。

+#### ページロード中のタイムアウトの遅延 -

WebExtension のバックグラウンドページとタイマー

+Firefox は現在のタブがロードされている間、 `setTimeout()` タイマーの発行を延期します。メインスレッドがアイドルと判断されるまで([window.requestIdleCallback()](/ja/docs/Web/API/Window/requestIdleCallback) と同様)、または load イベントが発生するまで起動が延期されます。 -

WebExtension のバックグラウンドページでは、タイマーが正しく動作しません。これはバックグラウンドページが実際には常にロードされたままではないからです。ブラウザ-は使用されていない場合はアンロードし、必要なときに復元することができます。これは拡張機能にはほとんど透過的ですが、いくつかのもの (JS タイマーを含む) はアンロード/リストアサイクル全体では動作しないので、バックグラウンドページは代わりにアラームを使うことを推奨します。これについての詳細は Migrate to Event Driven Background Scripts にあります。

+### WebExtension のバックグラウンドページとタイマー -

この記事の執筆時点では、 Chrome だけが上記の挙動を示していました - Firefox はまだアンロード/リストアの挙動をしていないので、タイマーは動作するでしょう。しかし、いくつかの理由から WebExtensions でタイマーを使わないようにするのはまだ良い考えです。

+[WebExtension](/ja/docs/Mozilla/Add-ons/WebExtensions) では、 `setTimeout()` は信頼できる動作をしません。拡張機能の作者は、代わりに [`alarms`](/ja/docs/Mozilla/Add-ons/WebExtensions/API/alarms) API を使用してください。 + +### 最大の遅延時間 + +Internet Explorer、Chrome、Safari、Firefox を含むブラウザーは、内部で遅延時間を 32 ビット符号付き整数値で保存します。このため 2,147,483,647 ms (約 24.8 日) より大きな遅延時間を使用すると整数値がオーバーフローして、その結果直ちに実行されるタイムアウトになります。 + +## 例 + +### タイムアウトの設定と取り消し + +以下の例はウェブページに 2 つのシンプルなボタンを置いており、`setTimeout()` および `clearTimeout()` のルーチンを実行します。1 番目のボタンを押下すると 2 秒後にアラートダイアログを呼び出すタイムアウトを設定して、`clearTimeout()` で使用するタイムアウト ID を保存します。2 番目のボタンを押下すると、このタイムアウトをキャンセルできます。 + +#### HTML + +```html + + + +
+``` + +#### JavaScript + +```js +let timeoutID; + +function setOutput(outputContent) { + document.querySelector('#output').textContent = outputContent; +} + +function delayedMessage() { + setOutput(''); + timeoutID = setTimeout(setOutput, 2*1000, '本当に遅い!'); +} + +function clearMessage() { + clearTimeout(timeoutID); +} +``` + +```css hidden +#output { + padding: .5rem 0; +} +``` -
    -
  1. Chrome との互換性のため
  2. -
  3. 将来、動作が変更された場合に問題が発生する可能性があるため
  4. -
+#### 結果 -

最大遅延時間

+{{EmbedLiveSample('Setting_and_clearing_timeouts')}} -

Internet Explorer、Chrome、Safari、Firefox を含むブラウザーは、内部で遅延時間を 32 ビット符号付き整数値で保存します。このため 2,147,483,647 ms (約 24.8 日) より大きな遅延時間を使用すると整数値がオーバーフローして、その結果直ちに実行されるタイムアウトになります。

+[`clearTimeout()` の例](/ja/docs/Web/API/clearTimeout#example)も参照してください。 -

仕様書

+## 仕様書 - - - - - - - - - - - - - - - - - - - - -
仕様書状態備考
{{SpecName('HTML WHATWG', 'webappapis.html#dom-settimeout', 'WindowOrWorkerGlobalScope.setTimeout()')}}{{Spec2("HTML WHATWG")}}最新の仕様で、メソッドを WindowOrWorkerGlobalScope ミックスインに移動。
{{SpecName("HTML WHATWG", "webappapis.html#dom-settimeout", "WindowTimers.setTimeout()")}}{{Spec2("HTML WHATWG")}}初回定義 (DOM Level 0)
+{{Specifications}} -

ブラウザーの互換性

+## ブラウザーの互換性 -

{{Compat("api.WindowOrWorkerGlobalScope.setTimeout")}}

+{{Compat}} -

関連情報

+## 関連情報 - +- [`core-js` にある `setTimeout` のポリフィルで、コールバックに引数を渡すことができるもの](https://github.com/zloirock/core-js#settimeout-and-setinterval) +- {{domxref("clearTimeout")}} +- {{domxref("setInterval()")}} +- {{domxref("window.requestAnimationFrame")}} +- {{domxref("queueMicrotask()")}} -- cgit v1.2.3-54-g00ecf