1. 8.6 タイマー
    2. 8.7 マイクロタスクのキュー
    3. 8.8 ユーザープロンプト
      1. 8.8.1 単純ダイアログ
      2. 8.8.2 印刷

8.6 タイマー

setTimeout()setInterval()メソッドは、著者がタイマーベースのコールバックをスケジュールできるようにする。

id = self.setTimeout(handler [, timeout [, ...arguments ] ])

timeoutミリ秒後にhandlerを実行するためにタイムアウトをスケジュールする。すべてのargumentsは直接handlerに渡される。

id = self.setTimeout(code [, timeout ])

timeoutミリ秒後にcodeを実行するためにタイムアウトをスケジュールする。

self.clearTimeout(id)

idで識別されるsetTimeout()またはsetInterval()で設定されたタイムアウトをキャンセルする。

id = self.setInterval(handler [, timeout [, ...arguments ] ])

timeoutミリ秒ごとにhandlerを実行するためにタイムアウトをスケジュールする。すべてのargumentsは直接handlerに渡される。

id = self.setInterval(code [, timeout ])

timeoutミリ秒ごとにcodeを実行するためにタイムアウトをスケジュールする。

self.clearInterval(id)

idで識別されるsetInterval()またはsetTimeout()で設定されたタイムアウトをキャンセルする。

タイマーは入れ子にすることができる。しかし、5つのそのようなネストされたタイマー後に、間隔は少なくとも4ミリ秒であることが強制される。

このAPIは、タイマーがスケジュールどおり正確に動作することを保証しない。CPU負荷やその他のタスクなどによる遅延が予想される。

遅延なく背中合わせに数ミリ秒のタスクを実行するために、飢えたユーザーインターフェイスを避けるために(およびCPUを占有するためのスクリプトを殺すブラウザーを避けるために)ブラウザーに戻って従いつつ、作業を実行する前の簡単なキューの次のタイマー:

function doExpensiveWork() {
  var done = false;
  // ...
  // this part of the function takes up to five milliseconds
  // set done to true if we're done
  // ...
  return done;
}

function rescheduleWork() {
  var id = setTimeout(rescheduleWork, 0); // preschedule next iteration
  if (doExpensiveWork())
    clearTimeout(id); // clear the timeout if we don't need it
}

function scheduleWork() {
  setTimeout(rescheduleWork, 0);
}

scheduleWork(); // queues a task to do lots of work

8.7 マイクロタスクのキュー

queueMicrotask

Support in all current engines.

Firefox69+Safari12.1+Chrome71+
Opera?Edge79+
Edge (Legacy)?Internet ExplorerNo
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?
self.queueMicrotask(callback)

指定されたcallbackを実行するためのマイクロタスクキューに入れる

queueMicrotask()メソッドは、著者がマイクロタスクキューでコールバックをスケジュールすることを可能にする。これは、JavaScript実行コンテキストスタックが次に空になるとコードを実行でき、これは、現在実行中のすべての同期JavaScriptが完了するまで実行されたときに発生する。これは、たとえばsetTimeout(f, 0)を使用する場合のように、イベントループに制御を戻すことはない。

著者は、大量のマイクロタスクをスケジュールすることが、大量の同期コードを実行するのと同じパフォーマンスの低下があることに注意する必要がある。 どちらも、ブラウザーがレンダリングなどの独自の作業を実行を妨げることになる。多くの場合、requestAnimationFrame()またはrequestIdleCallback()の方がよい選択である。 特に、次のレンダリングサイクルの前にコードを実行することが目標である場合、それはrequestAnimationFrame()の目的である。

次の例からわかるように、queueMicrotask()について考える最良の方法は、同期コードを再配置するためのメカニズムとして、現在実行中の同期JavaScriptが完了した直後にキューに入れられたコードを効果的に配置することである。

queueMicrotask() 使用する最も一般的な理由は、情報が同期的に利用できる場合でも、過度の遅延を発生させることなく、一貫した順序を作成することである。

たとえば、以前にロードされたデータの内部キャッシュも維持する、loadイベントを発生させるカスタム要素について考えてみる。ナイーブな実装は次のようになるだろう:

MyElement.prototype.loadData = function (url) {
  if (this._cache[url]) {
    this._setData(this._cache[url]);
    this.dispatchEvent(new Event("load"));
  } else {
    fetch(url).then(res => res.arrayBuffer()).then(data => {
      this._cache[url] = data;
      this._setData(data);
      this.dispatchEvent(new Event("load"));
    });
  }
};

しかし、このナイーブな実装には、ユーザーに一貫性のない動作が発生するという問題がある。たとえば、次のようなコードは

element.addEventListener("load", () => console.log("loaded"));
console.log("1");
element.loadData();
console.log("2");

"1, 2, loaded"(データをフェッチする必要がある場合)を記録することもあれば、"1, loaded, 2"(データがすでにキャッシュされている場合)を記録することもある。同様に、loadData()の呼び出し後、データが要素に設定されているかどうかは一貫性がない。

一貫した順序を取得するためには、queueMicrotask()を使用できる:

MyElement.prototype.loadData = function (url) {
  if (this._cache[url]) {
    queueMicrotask(() => {
      this._setData(this._cache[url]);
      this.dispatchEvent(new Event("load"));
    });
  } else {
    fetch(url).then(res => res.arrayBuffer()).then(data => {
      this._cache[url] = data;
      this._setData(data);
      this.dispatchEvent(new Event("load"));
    });
  }
};

キューに入れられたコードをJavaScript実行コンテキストスタックが空になった後になるように基本的に再配置することで、要素の状態の一貫した順序付けおよび更新が保証される。

queueMicrotask()のもう1つの興味深い使用法は、 複数の呼び出し元による作業の調整されていない"バッチ処理"を可能にすることである。たとえば、できるだけ早くどこかにデータを送信したいが、簡単に回避できる場合は、 複数のネットワークリクエストを行いたくないライブラリ関数について考えてみる。このバランスをとる1つの方法は次のようになる:

const queuedToSend = [];

function sendData(data) {
  queuedToSend.push(data);

  if (queuedToSend.length === 1) {
    queueMicrotask(() => {
      const stringToSend = JSON.stringify(queuedToSend);
      queuedToSend.length = 0;

      fetch("/endpoint", stringToSend);
    });
  }
}

このアーキテクチャでは、現在実行中の同期JavaScript内でsendData()を呼び出す複数の後続の呼び出しは、1つのfetch()呼び出しにまとめられるが、イベントループのタスクがフェッチを先取りすることはない(代わりに同様のコードで発生した場合のようにsetTimeout()を使用する)。

8.8 ユーザープロンプト

8.8.1 単純ダイアログ

window.alert(message)

指定されたメッセージを持つモーダルアラートを表示し、それを命令するユーザーに対して待機する。

result = window.confirm(message)

与えられたメッセージとともにモーダルOK/Cancelプロンプトを表示し、それを命令するユーザーに対して待機し、ユーザーがOKをクリックした場合はtrueを返し、ユーザーがCancelをクリックする場合はfalseを返す。

result = window.prompt(message [, default])

与えられたメッセージとともにモーダルテキストコントロールプロンプトを表示し、ユーザーがそれを閉じるのを待ち、ユーザーが入力した値を返す。ユーザーがプロンプトをキャンセルした場合、代わりにnullを返す。2番目の引数が存在する場合、指定された値がデフォルトとして使用される。

メディアデータを読み込むメディア要素のような、タスクまたはマイクロタスクに依存するロジックは、このメソッドが発動されるときに延期される。

8.8.2 印刷

Window/print

Support in all current engines.

Firefox1+Safari1.1+Chrome1+
Opera6+Edge79+
Edge (Legacy)12+Internet Explorer5+
Firefox Android114+Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android10.1+
window.print()

ページを印刷するようユーザーに指示する。