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 のインストールと設定のグラフィック チュートリアル

推薦する

JavaScript Three.js でテキストを作成する最初の経験

目次効果テキストの作成を開始するまずフォントローダーを作成するフォントライブラリを読み込むテキストジ...

docker compose を使用して FastDfs ファイル サーバーをインストールする詳細な例

ドッカーの作成 バージョン: '2' サービス: fastdfsトラッカー: ホスト...

MySQL 5.7 の同時レプリケーションにおける暗黙のバグの分析

序文当社の MySQL オンライン環境のほとんどはバージョン 5.7.18 を使用しています。このバ...

Alibaba Cloud Server Ubuntu 上の Workbench が MySQL に接続できない問題の解決策 (テスト済み)

過去 2 日間、ワークベンチが Alibaba Cloud Server に接続できない問題を解決す...

Windows で IP アドレスを指定してサーバーへのリモート アクセスを設定する方法

当社には、外部ネットワークからの干渉を受けることが多いサーバーが多数あります。侵入者はポート 338...

WeChatミニプログラムにナビゲーション機能を実装する方法

1. レンダリング2. 操作手順1. テンセントマップキーを申請する - 住所2. ミニプログラムの...

Linuxはscpコマンドを使用してファイルをローカルコンピュータにコピーし、ローカルファイルをリモートサーバーにコピーします。

以下のように表示されます。リモート サーバーのファイルをローカルにコピーします。 scp -r -P...

Linux (CentOS7) に Tomcat をインストールし、Tomcat をスタートアップ項目として設定します (tomcat8 を例に挙げます)

目次TomcatをインストールするTomcat 圧縮パッケージをダウンロードTomcatには3つの主...

Reactプロジェクトで画像を導入するいくつかの方法

imgタグは画像を導入しますreactは実際にはjsリーダー関数を介してページをレンダリングするため...

Vueのシンプルな状態管理ストアモードを理解する方法

目次概要1. store.jsを定義する2. store.js を使用するコンポーネント3. 成果を...

JTAを実装するためにAtomikosと組み合わせたTomcatについて

最近、プロジェクトは環境を切り替え、WebLogic を Tomcat に置き換えました。途中で発生...

Linuxでのソフトウェア(ライブラリ)の更新コマンドの詳しい説明

Ubuntu サーバーにパッケージをインストールする場合、sudo apt-get install ...

CSS で垂直方向の中央揃えを行う 7 つの方法の詳細なコード例

レイアウトを編集するとき、通常は水平センタリングと垂直センタリングを使用してデザインします。水平セン...

MySQL が InnoDB テーブルが独立したテーブルスペースか共有テーブルスペースかを判断する方法の詳細な説明

序文InnoDB はデータをテーブルスペースに保存します。デフォルト設定では、初期サイズが 10 M...

数百万のデータに対して MySQL クエリを最適化する 4 つの方法

目次1. 時間が経つにつれて限界が遅くなる理由2. 百万データシミュレーション1. 従業員テーブルと...