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

推薦する

MySQL の単一テーブル クエリ操作例の詳細な説明 [構文、制約、グループ化、集計、フィルタリング、並べ替えなど]

この記事では、MySQL の単一テーブル クエリ操作について説明します。ご参考までに、詳細は以下の通...

Reactイベントメカニズムソースコード分析

目次原理ソースコード分析委任されたイベントバインディングすべてのサポートされているイベントを聴くネイ...

SQL グループ化により重複を削除し、他のフィールドで並べ替える

必要:あるフィールドの同一項目を結合し、別の時間フィールドで並べ替えます。例:初めに テーブルから都...

MySQLが数十億のトラフィックをサポートする方法

目次1 マスター・スレーブの読み取り・書き込み分離1.1 コア2 マスタースレーブレプリケーション2...

Quickjs は JavaScript サンドボックスの詳細をカプセル化します

目次1. シナリオ2. 基盤となるAPIを簡素化する2.1 自動的に破棄を呼び出す2.2 VM値を作...

コードをセマンティックにする HTML のヒント

HTML のセマンティクスはありふれた問題のようです。Google で検索すると、セマンティクスに関...

MySQL alter ignore構文の詳細な説明

今日仕事中に、ビジネス側から次のような質問をされました。テーブルがあり、一意のフィールドを追加する必...

Linux で仮想コンソール セッションをロックする方法

共有システムで作業しているときは、他のユーザーが自分のコンソールを覗き込んで、自分が何をしているか見...

ハイパーリンクを開くターゲットのテスト

リンクのターゲット属性は、リンクが開く場所を決定します。その値は通常、_blank、_self、_p...

MySQLのスリープ関数の特殊現象例の詳しい説明

序文MySQL のスリープ システム機能は、実用的な適用シナリオが少なく、通常は実験的なテストに使用...

jQueryの競合問題を解決する方法

フロントエンド開発において、$ は jQuery の関数です。$ のパラメータが異なると、実装される...

Dockerカスタムブリッジdocker0とdockerのコマンド操作の開始、終了、再起動

質問会社がサーバーを移行した後、デフォルトで作成された docker0 ブリッジが会社の外部ネットワ...

Access_Tokenの統合管理を実現するミニプログラム開発

目次TOKEN タイマーリフレッシュ2. access_tokenの内部設計2.1 access_t...

Linux のソフトリンクとハードリンクの詳細な説明

目次1. ファイルとディレクトリの基本的な保存2. Inコマンドの紹介(1)lnコマンドの基本情報を...

Vue ローカルコンポーネントデータ共有 Vue.observable() の使用

コンポーネントが詳細になるにつれて、複数のコンポーネントが状態を共有する状況に遭遇するでしょう。Vu...