JavaScript/TypeScript で同時リクエスト制御を実装するためのサンプルコード

JavaScript/TypeScript で同時リクエスト制御を実装するためのサンプルコード

シナリオ

リクエストが 10 件あるが、同時リクエストの最大数は 5 件で、リクエスト結果が必要であるとします。これは、単純な同時リクエスト制御です。

シミュレーション

setTimeoutを使用してリクエストの簡単なシミュレーションを実行します

startTime を Date.now() とします。
const タイムアウト = (タイムアウト: 数値, ret: 数値) => {
 戻り値 (idx?: 任意) =>
 新しいPromise((resolve) => {
  タイムアウトを設定する(() => {
  const compare = Date.now() - startTime;
  console.log(`${Math.floor(compare / 100)}00 で return`, ret);
  解決(idx);
  }、 タイムアウト);
 });
};

定数timeout1 = timeout(1000, 1);
定数timeout2 = timeout(300, 2);
定数timeout3 = タイムアウト(400, 3);
定数timeout4 = タイムアウト(500, 4);
定数timeout5 = タイムアウト(200, 5);

このようにリクエストをシミュレートすることで、本質はPromiseになります

同時実行制御がない場合

定数実行 = 非同期() => {
 開始時刻 = Date.now();
 Promise.all([ を待つ
 タイムアウト1()、
 タイムアウト2()、
 タイムアウト3()、
 タイムアウト4()、
 タイムアウト5()、
 ]);
};

走る();

200で5を返す
300で2を返す
400で3を返す
500で4を返す
1000で1を返す

出力は 5 2 3 4 1 となっており、タイムアウト時間に応じて出力されていることがわかります。

同時実行条件

同時接続の最大数が2であると仮定して、クラスを作成します

クラスConcurrent {
 プライベートmaxConcurrent: 数値 = 2;

 コンストラクター(count: number = 2) {
 this.maxConcurrent = カウント;
 }
}

最初の同時実行制御

考えてみてください。同時実行の最大数に応じて Promise 配列を分割します。Promise が満たされた場合はそれを削除し、保留中の Promise を追加します。 Promise.raceはこの要件を満たすのに役立ちます

クラスConcurrent {
 プライベートmaxConcurrent: 数値 = 2;

 コンストラクタ(count: number = 2) {
 this.maxConcurrent = カウント;
 }
 パブリック非同期useRace(fns: Function[]) {
 const 実行中: any[] = [];
 // 同時実行数に応じて Promise を追加します // Promise はインデックスをコールバックするので、どの Promise が解決されたかを知ることができます (let i = 0; i < this.maxConcurrent; i++) {
  (fns.長さ)の場合{
  const fn = fns.shift()!;
  実行中.push(fn(i));
  }
 }
 定数ハンドル = 非同期() => {
  if (fns.length) {
  const idx = await Promise.race<number>(running);
  const nextFn = fns.shift()!;
  // 完了した Promise を削除し、新しいものを runing.splice(idx, 1, nextFn(idx)); に配置します。
  ハンドル();
  } それ以外 {
  // 配列がクリアされている場合は、実行される Promise がないことを意味します。これを Promise.all に変更できます。
  Promise.all(実行中) を待機します。
  }
 };
 ハンドル();
 }
}

定数実行 = 非同期() => {
 const コンカレント = 新しい Concurrent();
 開始時刻 = Date.now();
 同時実行のuseRace([タイムアウト1、タイムアウト2、タイムアウト3、タイムアウト4、タイムアウト5])を待機します。
};

300で2を返す
700で3を返す
1000で1を返す
1200に5を返す
1200に4に戻る

出力が変化したことがわかります。なぜこのようなことが起こるのでしょうか? 分析してみましょう。同時接続の最大数は 2 です。

// 最初に実行するのは 1 2
1 完了するには1000MSかかります
2 300 MS必要

2 が実行されると、タイムラインは 300 になります。2 を削除します。3 を追加します。3 の実行を開始します。
3 は 400 ミリ秒かかります。実行時間は 700 ミリ秒になります。3 を削除します。4 を追加します。4 の実行を開始します。
4 500MSが必要
タイムラインが1000MSに達すると、1つが実行されて削除され、5つが追加され、5つが開始されます。
タイムラインが1200MSに到達し、ステップ4と5が同時に実行されます。

2番目のオプション

awaitメカニズムを使うことができます。これは実際にはちょっとしたトリックです

await 式は、現在の async 関数の実行を一時停止し、Promise が完了するまで待機します。 Promise が満たされると、コールバックの解決関数パラメータが await 式の値として使用され、非同期関数の実行が続行されます。

現在の同時リクエスト数が最大同時リクエスト数を超える場合は、新しい Promise を設定して待機できます。他のリクエストが完了するのを待機している場合は、解決して待機を削除します。そのため、現在の同時リクエスト数と、解決コールバック関数を格納する配列という 2 つの新しい状態を追加する必要があります。

クラスConcurrent {
 プライベートmaxConcurrent: 数値 = 2;
 プライベートリスト: Function[] = [];
 プライベートcurrentCount: 数値 = 0;

 コンストラクタ(count: number = 2) {
 this.maxConcurrent = カウント;
 }
 パブリック非同期追加(fn:関数) {
 this.currentCount += 1;
 // 同時接続の最大数が最大値を超えた場合 if (this.currentCount > this.maxConcurrent) {
  // wait は Promise であり、resolve が呼び出される限り満たされます。const wait = new Promise((resolve) => {
  this.list.push(解決) を実行します。
  });
  //resolve が呼び出されない場合、ここで await wait がブロックされます。
 }
 //関数を実行する await fn();
 this.currentCount -= 1;
 if (this.list.length) {
  // 解決を取り出して呼び出すと、待機が完了し、以下を実行できるようになります。const resolveHandler = this.list.shift()!;
  ハンドラを解決します。
 }
 }
}

定数実行 = 非同期() => {
 const コンカレント = 新しい Concurrent();
 開始時刻 = Date.now();
 同時実行を追加します(タイムアウト1);
 同時実行を追加します(タイムアウト2)。
 同時実行を追加します(timeout3);
 同時実行を追加します(タイムアウト4)。
 同時実行を追加します(timeout5);
};

走る();

300で2を返す
700で3を返す
1000で1を返す
1200に5を返す
1200に4に戻る

要約する

どちらの方法でも並行制御を実現できますが、実装方法が異なります。主に Promise によって実装されます。また、実装方法は異常事態を考慮しておらず、自分で追加できる可能性があります。

これで、JavaScript/TypeScript で同時リクエスト制御を実装するサンプルコードに関する記事は終了です。JavaScript の同時リクエスト制御に関する詳細については、123WORDPRESS.COM の過去の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • JavaScript のプライベート クラス フィールドと TypeScript のプライベート修飾子の詳細な説明
  • JS デコレータ パターンと TypeScript デコレータ
  • TypeScriptでのRxJSの簡単な使い方の詳しい説明
  • Vue.js で TypeScript を使用する方法
  • JavaScript と TypeScript における void の具体的な使用法
  • Typescript nodejs 依存性注入実装コードの詳細な説明
  • vue + typescript + video.js でストリーミングビデオ監視機能を実現
  • TypeScriptを使用してNode.jsアプリケーションを開発する方法を教えます
  • JavaScriptとTypeScriptの関係

<<:  js 加算、減算、乗算、除算の正確な計算方法のサンプルコード

>>:  MySQL 5.7.17 winx64 のインストールと設定方法のグラフィックチュートリアル

推薦する

Linux コマンドを使用してオーディオ形式を変換および結合する方法

FFmpeg flacをインストールする eric@ray:~$ sudo apt install ...

Tcl言語に基づくシンプルなネットワーク環境を構成するプロセスの分析

1. Tclスクリプトファイルcircle.tclコードコメント #シミュレーションに必要なプロパテ...

Windows で mysql5.7.21 をインストールするための詳細なチュートリアル

この記事では、参考までにMySQL 5.7.21のインストールチュートリアルを紹介します。具体的な内...

PXEを使用してLinuxシステムを自動的に展開する方法

目次背景DHCPの設定DHCP ファイル (動的ホスト構成プロトコル) の編集tftp 設定sysl...

Nginx_geo モジュールを使用して CDN スケジュールを設定する方法

NginxのGeoモジュールの紹介geo ディレクティブは、ngx_http_geo_module ...

HTML で JavaScript の全選択/全選択解除操作を実行するサンプル コード

コードをコピーコードは次のとおりです。 <html> <ヘッド> <m...

MySQLデータのグループ化の詳細な説明

グループを作成グループ化は、SELECT ステートメントの GROUP BY 句で設定されます。例:...

Windows 上の MySQL 5.6 で my.ini 設定ファイルを見つける方法

忘れてしまった場合に後で戻って確認できるようにメモしておいてください。問題の説明:今日はちょっとした...

MySQL における 8 つの一般的な SQL 使用例

序文MySQL は、2016 年もデータベースの人気において力強い成長傾向を維持し続けました。 My...

Ubuntuでネットワークルーティングテーブルを表示する方法

Linux におけるルーティングとルーティング テーブルとは何ですか?ルーティングのプロセスとは、ネ...

docker 環境でのデータベース バックアップ (postgresql、mysql) のサンプル コード

目次posgresql バックアップ/リストアMySQL バックアップ/復元posgresql バッ...

CSSでemを開く正しい方法の詳細な説明

「通常 1em=16px」と言うのはなぜですか?ユーザーのブラウザによってレンダリングされるデフォル...

Linux サーバーのグラフィック カードのクラッシュの解決策

ログインインターフェースの解像度が特に大きい場合、グラフィカルインターフェース全体が特に大きくなり、...

Linuxカーネルスケジューラソースコード初期化の分析

目次1. はじめに2. スケジューラの基本概念2.1. 実行キュー (rq) 2.2 スケジューリン...

Linux で大容量メモリ ページを持つ Oracle データベースを最適化する方法

序文PC サーバーは今日まで発展を続け、パフォーマンスにおいて大きな進歩を遂げてきました。 64ビッ...