From d6707e7bc591b7526269b1cf52bb9db8eeb940b4 Mon Sep 17 00:00:00 2001 From: Masahiro FUJIMOTO Date: Wed, 9 Mar 2022 22:53:14 +0900 Subject: JavaScript タイマー関係のメソッドを移行 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- files/ja/web/api/settimeout/index.html | 420 --------------------------------- 1 file changed, 420 deletions(-) delete mode 100644 files/ja/web/api/settimeout/index.html (limited to 'files/ja/web/api/settimeout/index.html') diff --git a/files/ja/web/api/settimeout/index.html b/files/ja/web/api/settimeout/index.html deleted file mode 100644 index 865c4679a6..0000000000 --- a/files/ja/web/api/settimeout/index.html +++ /dev/null @@ -1,420 +0,0 @@ ---- -title: WindowOrWorkerGlobalScope.setTimeout() -slug: Web/API/setTimeout -tags: - - API - - HTML DOM - - Intervals - - JavaScript timers - - MakeBrowserAgnostic - - Method - - NeedsCompatTable - - NeedsMarkupWork - - NeedsUpdate - - Reference - - Timers - - WindowOrWorkerGlobalScope - - setTimeout -translation_of: Web/API/WindowOrWorkerGlobalScope/setTimeout -original_slug: Web/API/WindowOrWorkerGlobalScope/setTimeout ---- -
{{APIRef("HTML DOM")}}
- -

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

- -

構文

- -
var timeoutID = scope.setTimeout(function[, delay, arg1, arg2, ...]);
-var timeoutID = scope.setTimeout(function[, delay]);
-var timeoutID = scope.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 で指定された関数に渡す追加の引数です。
-
- -
-

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

-
- -

返値

- -

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

- -

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

- -

同じオブジェクト (ウィンドウやワーカー) では、後に setTimeout()setInterval() を呼び出しても タイムアウト 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(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);
-};
-
-myArray.myMethod(); // prints "zero,one,two"
-myArray.myMethod(1); // prints "one"
- -

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

- -
setTimeout(myArray.myMethod, 1.0*1000); // 1秒後に "[object Window]" と表示
-setTimeout(myArray.myMethod, 1.5*1000, '1'); // 1.5秒後に "undefined" と表示
- -

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

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

考えられる解決策

- -

この問題の一般的な解決策は、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
- -

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

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

他に考えられる "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);
-};
- -
: これら 2 つの置き換えにより、IE のタイマーで HTML5 標準の、コールバック関数に任意の引数を渡すことも可能になります。よって、ポリフィルとしても使用できます。Callback arguments の節をご覧ください。
- -

新機能のテスト:

- -
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
-
- -
: JavaScript 1.8.5 で、関数のすべての呼び出しに対して 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, 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
-
- -

- -

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

- -

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

- -

文字列リテラルの使用

- -

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

- -
// 推奨
-window.setTimeout(function() {
-  alert('Hello World!');
-}, 500);
-
-// 非推奨
-window.setTimeout("alert('Hello World!');", 500);
-
- -

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

- -

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

- -

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

- -

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

- -

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

- -
function cb() { f(); setTimeout(cb, 0); }
-setTimeout(cb, 0);
-setInterval(f, 0);
- -

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

- -

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

- -

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

- -
-

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

-
- -
-

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

-
- -

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

- -

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

- -

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

- -

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

- -
-

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

-
- -

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

- -

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

- -

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

- - - -

タイムアウトの遅延

- -

前出の "制限" に加えて、ページ内 (または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 で呼び出したとしても、直ちに実行するのではなくキューに載せて、次の機会に実行するようスケジューリングされるためです。現在実行中のコードはキューにある関数を実行する前に完了しなければならず、このために実行結果の順序が想定どおりにならない場合があります。

- -

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

- -

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

- -

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

- -
    -
  1. Chrome との互換性のため
  2. -
  3. 将来、動作が変更された場合に問題が発生する可能性があるため
  4. -
- -

最大遅延時間

- -

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

- -

仕様書

- - - - - - - - - - - - - - - - - - - - - -
仕様書状態備考
{{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)
- -

ブラウザーの互換性

- -

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

- -

関連情報

- - -- cgit v1.2.3-54-g00ecf