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

推薦する

Nest.js パラメータ検証とカスタム戻りデータ形式の詳細な説明

0x0 パラメータ検証Nest.jsでは、パラメータ検証業務のほとんどをパイプライン方式で実装してい...

スーパーバイザーによるDockerfileのマルチサービスイメージパッケージ操作

Dockerfileの作成yumソースを設定する cd /tmp/docker vim Docker...

MySQL 学習ノート: 完全な SELECT ステートメントの使用例と詳細な説明

この記事では、MySQL 学習ノートの select ステートメントの完全な使用方法を例を使用して説...

Nginx の break と last の違いの詳細な分析

まずは違いについて話しましょう最後に、書き換えられたルールは、次の場所と一致させるために書き換えられ...

HTMLのテーブルの内容は中央に水平と垂直に表示されます

CSSスタイルファイルで指定 #class td /*表のテキストを左右上下に揃えるように設定する*...

Ckeditor + Ckfinderを使用したJavaScriptファイルアップロードケースの詳細な説明

目次1. 準備2. 減圧3. 統合を開始する1. 準備Ckeditor_4.5.7_full + C...

ポップアップ効果を実現するにはjsを使用します

この記事の例では、ポップアップ効果を実現するためのjsの具体的なコードを参考までに共有しています。具...

div の幅が width:100% に設定されていて、パディングまたはマージンが親要素を超えてしまう問題の解決方法

序文この記事では、div の幅を 100% に設定し、親要素を超えてパディングまたはマージンを設定す...

文字列の最初の文字を取得してテキストアイコン機能を実現する純粋なCSS

CSS でテキストアイコンを実装する方法 /*アイコンスタイル*/ .nav-icon-norma...

超詳細なMySQL使用仕様の共有

最近、データベース関連の操作が多くなり、会社の既存の仕様はあまり包括的ではありません。インターネット...

JavaScript ベースで年・月・日の 3 階層連携を実現

この記事では、年、月、日の3段階のリンクを実現するためのJavaScriptの具体的なコードを参考ま...

ウェブサイトのホームページを作成するための基本原則

1. ウェブサイトのホームページのハイパーテキスト ドキュメントの構成構造は、ユーザーの注意をできる...

Vuex ステートマシンの簡単な理解とサンプルアプリケーション

目次1. 概念の素早い理解: 1. コンポーネント間でデータを共有する方法: 2. vuex とは何...

Apache Spark 2.0ジョブは完了するまでに長い時間がかかります

現象Apache Spark 2.x を使用すると、Spark ジョブがすべて完了しているにもかかわ...

MySQL の主キーがクエリを高速化するために数値を使用するか UUID を使用するかについての簡単な分析

実際の開発では、MySQL の主キーは重複できず、主キーが自動的にインクリメントされることがあります...