インタビュアーは、「リクエストが 100 件ある場合、Promise を使用して同時実行性を制御するにはどうすればよいですか?」と尋ねました。

オープニング

最新の Web 開発では、非同期リクエストは不可欠な部分になっています。しかし、複数のリクエストを同時に処理する必要がある場合、リクエスト間の競合や混乱を避けるにはどうすればよいでしょうか? これが今日説明するトピック、つまり Promise を使用して同時リクエストを制御する方法です。

JavaScript では、非同期同時タスクの制御はPromise.all()Promise.race()、などのasync/awaitさまざまな方法で実現できます。メソッドを使用して同時実行制御を実装する例を次に示しますPromise.all()

Promise.all()

const urls = ["url1", "url2", ... ,"url100"]; 
const maxConcurrentNum = 10; // 最大并发数 
// 数组分块,chunk表示每批次数量,返回数组二维数组 
function chunk(arr, chunk) { 
  let result = []; 
  for (let i = 0, len = arr.length; i < len; i += chunk) { 
    result.push(arr.slice(i, i + chunk)); 
   } 
   return result; 
 }

// 异步请求方法 
function fetchUrl(url) { 
  return new Promise((resolve, reject) => { 
    fetch(url) 
      .then(res => resolve(res)) 
      .catch(err => reject(err)); 
     }); 
   }

// 对url数组进行分块处理
const chunkedUrls = chunk(urls, maxConcurrentNum);

(async function () {
  try {
    for (let urls of chunkedUrls) {
      const promises = urls.map(url => fetchUrl(url));
      // 等待所有promises完成执行,并将结果存入results数组中
      const results = await Promise.all(promises);
      console.log('results:', results);
    }
  } catch (err) {
   console.error(err);
  }
})();

maxConcurrentNum上記のコードは、配列を同じ数の複数の小さな配列に分割することで同時実行数を制御し、毎回最大 1 つの同時リクエストのみを開きます。リクエストのグループが完了するたびに新しいリクエストのバッチを送信することで、非同期タスクの同時実行制御を実現できます。

Promise.race()

以下は、Promise.race() メソッドを使用して同時実行性を制御するコード例です。

const promiselist = [];
for (let i = 0; i < 100; i++) {
  const promise = fetch(`https://example.com/data${i}.json`);
  promiselist.push(promise);
}
Promise.race(promiselist)
  .then(response => {
    // handle the fastest response here
  })
  .catch(error => {
    console.error(error);
  });

非同期/待機

以下は、async/await を使用して同時リクエストを制御するサンプル コードです。

async function getData() {
  const promiselist = [];
  for (let i = 0; i < 100; i++) {
    const promise = fetch(`https://example.com/data${i}.json`);
    promiselist.push(promise);
  }
  const responses = await Promise.all(promiselist);
  for (const response of responses) {
    // handle each response here 
  }
}

getData().catch(error => {
  console.error(error);
});

上記のコードでは、まず非同期関数を作成し、この関数で for ループを使用してすべてのリクエストを送信し、各リクエストの Promise オブジェクトを配列に格納します。次に、await キーワードを使用して、すべてがPromise对象解決されるまで非同期的に待機し、解決された値を配列に保存します。最後に、各応答を処理しながら配列を反復処理します。

最速のリクエストを待つ必要があるだけの場合は、Promise.race()メソッドを使用して、それを 1 つの にラップすることができますasync函数このアプローチは、Promise.all()使用されているものと似ていますが、異なる Promise メソッドを使用しているだけです。

以下は、メソッドasync/awaitを使用して同時リクエストを制御するためのサンプル コードですPromise.race()

async function getData() {
  const promiselist = [];
  for (let i = 0; i < 100; i++) {
    const promise = fetch(`https://example.com/data${i}.json`);
    promiselist.push(promise);
  }
  const response = await Promise.race(promiselist);
  // handle the fastest response here
}

getData().catch(error => {
  console.error(error);
});

上記のコードでは、 を使用しasync函数て を生成しPromise对象、次にPromise.race()メソッドを使用して最速の解決を待機しPromise对象、その解決値を処理します。

Promise.all()Promise.race()、 などのメソッドに加えてasync/await、次のような同時リクエストを制御する他のメソッドも考えられます。

  1. カウンタを手動で制御する
    変数を使用して手動でカウントし、同時リクエストの数を制御できます。たとえば、ループ内で、カウンタが同時リクエストの最大数に達すると、それを使用してリクエストが完了するまで待機し、次のリクエストを許可するためにカウンタをインクリメントします。
    カウンタを手動で制御するサンプルコードを次に示します。
function getData() {
 const limit = 5; // maximum concurrent requests
 const dataUrls = ['https://example.com/data1.json', 
                   'https://example.com/data2.json',
                   'https://example.com/data3.json',
                   'https://example.com/data4.json',
                   'https://example.com/data5.json',
                   'https://example.com/data6.json'];

 let counter = 0;
 const getDataPromise = dataUrl => {
   return new Promise((resolve, reject) => {
     fetch(dataUrl)
       .then(response => {
         counter--;
         resolve(response);
       })
       .catch(error => {
         counter--;
         reject(error);
       });
   });
 };

 const getDataPromises = dataUrls.map(dataUrl => {
   if (counter < limit) {
     counter++;
     return getDataPromise(dataUrl);
   } else {
     return new Promise(resolve => {
       const interval = setInterval(() => {
         if (counter < limit) {
           counter++;
           clearInterval(interval);
           resolve(getDataPromise(dataUrl));
         }
       }, 100);
     });
   }
 });

 Promise.all(getDataPromises)
   .then(responses => {
     for (const response of responses) {
       // handle each response here
     }
   })
   .catch(error => {
     console.error(error);
   });
}

getData();

上記のコードでは、カウンタを手動で使用して同時リクエストの最大数を制御し、それを使用してsetInterval函数利用可能なリクエスト スロットが空くまで待機します。

  1. サードパーティのライブラリを使用する

さらに、 や など、使用できるサードパーティ ライブラリがいくつかありasync.jsますp-limitp-limitは専用のコントロールですPromise并发的小型库p-limit詳細と例については、ドキュメントを参照してください。

要約する

Promise使い方をマスターすれば、同時リクエストに簡単に対処できるようになり、Web アプリケーションがよりスムーズになり、ユーザーの満足度が高まります。したがって、同時リクエストが悪夢にならないように、Promise問題の解決をお手伝いさせてください。

1. フロントエンド面接クエスチョンバンク(面接に必要)オススメ度: ★★★★★            

宛先:フロントエンド面接の質問バンク

おすすめ

転載: blog.csdn.net/weixin_42981560/article/details/131822584