js で 0ms 遅延タイマーを実装するいくつかの方法

js で 0ms 遅延タイマーを実装するいくつかの方法

ここ二日間、「タイムリーな setTimeout を実装するには?」を紹介する記事を見ました。 》、この記事はインタビューの質問から生まれました: setTimeout を時間どおりに実行する方法はありますか?詳細については、付録 [1] を参照してください。これを読んだ後、setTimeout 関数を詳しく調べてみたくなったので、MDN の関連ドキュメントを再度確認しました。[最小遅延 >= 4ms] と記載されていたため、setTimeout を使用して 0ms の遅延タイマーを実装することはできません。実装したい場合は、参考リンク [2] を参照してください。著者の実装アイデアは、postMessage を通じてシミュレートし、setTimeout の制限を回避して 0ms の遅延タイマーを実装することです。簡単に言うと、マクロタスクを開始してコールバックを実行することです。どのように実装されているかを見てみましょう。

(関数() {
 var タイムアウト = [];
 var messageName = "ゼロタイムアウトメッセージ";
 // setTimeoutと同様だが、関数引数のみを取る。
 // 時間引数なし(常にゼロ)および引数なし(クロージャを使用する必要があります)
 関数setZeroTimeout(fn) {
  タイムアウトをプッシュします(fn);
  window.postMessage(メッセージ名、"*");
 }
 関数handleMessage(イベント) {
  if (event.source == window && event.data == messageName) {
   イベントの伝播を停止します。
   タイムアウトの長さが0より大きい場合
    var fn = timeouts.shift();
    関数fn();
   }
  }
 }

 window.addEventListener("メッセージ", handleMessage, true);

 // ウィンドウ オブジェクトに追加したいものを 1 つ追加します。
 ウィンドウをゼロタイムアウトに設定します。
})();

作者はデモページも公開している[3]。setTimeout(0)と比較すると、私のブラウザでの実行結果は以下のようになる。

setZeroTimeout の 100 回の反復には 15 ミリ秒かかりました。
setTimeout(0) の 100 回の反復には 488 ミリ秒かかりました。

比較結果によると、setZeroTimeout は setTimeout よりも数百倍高速に実行され、大きな改善が見られます。本日お話ししたいのは、上記の方法に加えて、0 ミリ秒の遅延タイマーを実装するためにどのような他の方法が使用できるかということです。まず、カスタム タイマーが非同期であることを確認する必要があります。次に、できるだけ早く実行されるようにします。非同期性について言えば、js はいくつかのソリューションを提供しており、それを 1 つずつ検証することができます。

さまざまな実装方法について詳しく説明する前に、合意によって提供される setTimeout 比較バージョンは次のとおりです。カスタム実装スキームの実行時間を setTimeout バージョンと比較します。コードは比較的単純です。

(関数() {
 i = 0 とします。
 定数開始 = Date.now();
 関数テスト() {
  i++ < 100の場合{
   テストタイムアウトを設定します。
  } それ以外 {
   console.log('setTimeout 実行時間:', Date.now() - 開始);
  }
 }
 テストタイムアウトを設定します。
})();

キューマイクロタスク

queueMicrotask API を使用すると、マイクロタスクを追加できます。使い方は比較的簡単です。コールバック関数を渡すだけです。具体的な実装は次のとおりです。

(関数() {
 関数setZeroTimeout(fn) {
  キューマイクロタスク(fn);
 }
 i = 0 とします。
 定数開始 = Date.now();
 関数テスト() {
  i++ < 100の場合{
   setZeroTimeout(テスト);
  } それ以外 {
   console.log('setZeroTimeout 実行時間:', Date.now() - 開始);
  }
 }
 setZeroTimeout(テスト);
})();

setTimeout バージョンと比較すると、最終結果は次のようになります。

setZeroTimeout 実行時間: 2
setTimeout 実行時間: 490

この API については MDN に詳しい紹介があるので、ここでは詳しく説明しません。仕様書によると、ほとんどの場合、requestAnimationFrame() や requestIdleCallback() などの API を使用することをお勧めします。queueMicrotask はレンダリングをブロックするため、多くの場合これは良い方法ではありません。

非同期/待機

async/await はフロントエンド開発者にとってすでに不可欠であり、ここでもこれを使用して次のことを実現できます。

(関数() {
 非同期関数setAsyncTimeout(fn) {
  Promise.resolve().then(fn);
 }
 i = 0 とします。
 定数開始 = Date.now();
 非同期関数テスト(){
  (i++ < 100) の場合 {
   setAsyncTimeout(テスト) を待機します。
  } それ以外 {
   console.log('setAsyncTimeout 実行時間:', Date.now() - 開始);
  }
 }
 setAsyncTimeout(テスト);
})();

setTimeout バージョンと比較すると、最終結果は次のようになります。

setAsyncTimeout 実行時間: 2
setTimeout 実行時間: 490

面倒でも構わないのであれば、Promise を使用して実装することもできます。実際、それらはすべて似ており、コードが少し増えるだけなので、ここでは省略します。

メッセージチャネル

MessageChannel を使用すると、新しいメッセージ チャネルを作成し、その 2 つの MessagePort プロパティを通じてデータを送信できます。MessageChannel は、ワーカー/iframe 間の通信など、ポート間の通信を実現するためのポートの概念を提供します。

(関数() {
 定数チャンネル = 新しいメッセージチャンネル();
 関数setMessageChannelTimeout(fn) {
  チャネルポート2のpostMessage(null);
 }
 チャネル.port1.onmessage = 関数() {
    テスト();
 };
 i = 0 とします。
 定数開始 = Date.now();
 関数テスト() {
  i++ < 100の場合{
   メッセージチャネルタイムアウトを設定します(テスト)。
  } それ以外 {
   console.log('setMessageChannelTimeout 実行時間:', Date.now() - 開始);
  }
 }
 メッセージチャネルタイムアウトを設定します(テスト)。
})();

setTimeout バージョンと比較すると、最終結果は次のようになります。

setMessageChannelTimeout 実行時間: 4
setTimeout 実行時間: 490

3 番目の方法は、MessageChannel がマクロタスクを生成するのに対し、他の 2 つの方法はマイクロタスクであるため、前の 2 つの方法よりも実行に時間がかかります。マイクロタスクが最初に実行され、メイン スレッドをブロックするため、時間がかかります。

やっと

この記事では、js が提供する非同期ソリューションに基づいた 3 つの実装方法を紹介します。実装自体は複雑ではありません。

付録

【1】https://mp.weixin.qq.com/s/QRIXBoKr2dMgLob3Atq9-g
【2】https://dbaron.org/log/20100309-faster-timeouts
【3】https://dbaron.org/mozilla/zero-timeout

これで、js で 0ms 遅延タイマーを実装するいくつかの方法についての記事は終了です。より関連性の高い js 遅延タイマー コンテンツについては、123WORDPRESS.COM で以前の記事を検索するか、次の関連記事を引き続き参照してください。今後も 123WORDPRESS.COM を応援していただければ幸いです。

以下もご興味があるかもしれません:
  • JavaScriptタイマーの詳細な説明
  • JavaScript タイマーの詳細
  • 期間限定フラッシュセール機能を実現するJavaScriptタイマー
  • 画像のシームレスなスクロールを実現する JavaScript タイマー
  • JSタイマーを使用して要素を移動する
  • JavaScript タイマー原理の詳細な説明

<<:  CentOS 7.2 に SuPHP をインストールするための詳細な手順

>>:  macOS での MySQL 8.0.16 のインストールと設定のグラフィック チュートリアル

推薦する

時刻を保存するために適切な MySQL の datetime 型を選択する方法

データベースを構築してプログラムを書くとき、日付と時刻の使用は避けられません。データベースには、ti...

docker compose の使い方の詳しい説明

目次Docker Compose の使用シナリオ基本的なデモ基本的な操作とメンテナンスdocker-...

MySQLは実際に分散ロックを実装できる

序文前回の記事では、eコマース シナリオでのフラッシュ セールの例を通じて、モノリシック アーキテク...

JavaScriptエラーキャプチャの詳細な説明

目次1. 基本的な使い方とロジック2. 特徴3. エラーオブジェクト4. キャッチアンドスロー戦略の...

impress.js プレゼンテーション層フレームワーク (デモツール) - 初めての体験

半年もブログを書いていなかったので、少し恥ずかしいです... 正月休みは、Dota をプレイしたり ...

Docker.v19 で Docker Compose オーケストレーション ツールをインストールして構成する方法

1. Compose の紹介Compose は、マルチコンテナ Docker アプリケーションを定義...

Vueライフサイクルの違いの詳細な説明

ライフサイクル分類vue の各コンポーネントは独立しており、各コンポーネントには独自のライフサイクル...

初心者がHTMLタグを学ぶ(1)

初心者は、いくつかの HTML タグを理解することで HTML を学習できます。この入門書は、初心者...

デザイン理論: コンテンツプレゼンテーションのための 10 のヒント

<br /> テキスト、記号、リンクの3つの側面に焦点を当て、主に中国語で、個人的な執筆...

MySQL 集計統計データの低速クエリの最適化

前面に書かれた注文テーブル、アクセス記録テーブル、商品テーブルなど、日常生活でデータベースを操作する...

IE イメージ ツールバーを無効にする

IE6 で試してみたところ、ツールバーが表示されました。オプションに「イメージ ツールバーを有効にす...

VUE + OPENLAYERSがリアルタイムポジショニング機能を実現

目次序文1. ラベルスタイルを定義する2. GeoJSONデータのシミュレーション3. Vercto...

LinuxシステムにISOファイルをインストールする方法

Linux システムで iso ファイルをインストールするにはどうすればいいですか?インストール手順...

25 個の CSS フレームワーク、ツール、ソフトウェア、テンプレートを共有

スプライトカウダウンロード CSS リントダウンロード プレフィックスダウンロード 1140px C...