在前端开发中,并发请求是一个常见需求,尤其是在需要同时获取多个数据源的场景。合理地管理并发请求可以提升性能、优化用户体验,并降低服务器压力。以下是几种实现并发请求的方法及其控制策略。

使用 Promise.all 实现并发请求

Promise.all 是一种简单高效的方式,可以同时发送多个请求,并在所有请求完成后统一处理结果。

const fetchData1 = fetch('https://api.example.com/data1');
const fetchData2 = fetch('https://api.example.com/data2');
const fetchData3 = fetch('https://api.example.com/data3');
Promise.all([fetchData1, fetchData2, fetchData3])
 .then(responses => Promise.all(responses.map(response => response.json())))
 .then(data => console.log(data)) // 输出 [data1, data2, data3]
 .catch(error => console.error('请求出错:', error));

这种方法简单直观,但无法控制并发数量,适合请求数量较少的场景。

控制并发请求数量

在请求数量较多时,可以通过自定义函数限制并发数量。例如,以下代码实现了最大并发数控制:

const concurrencyRequest = (urls, maxNum) => {
 return new Promise(resolve => {
   if (urls.length === 0) {
     resolve([]);
     return;
   }
   const results = [];
   let index = 0;
   let count = 0;
   const request = async () => {
     if (index === urls.length) return;
     const i = index;
     const url = urls[index];
     index++;
     try {
       const resp = await fetch(url);
       results[i] = await resp.json();
     } catch (err) {
       results[i] = err;
     } finally {
       count++;
       if (count === urls.length) resolve(results);
       else request();
     }
   };
   const times = Math.min(maxNum, urls.length);
   for (let i = 0; i < times; i++) request();
 });
};
// 测试
const urls = Array.from({ length: 20 }, (_, i) => `https://jsonplaceholder.typicode.com/todos/${i + 1}`);
concurrencyRequest(urls, 3).then(res => console.log(res));

此方法通过限制同时运行的请求数量,避免了过多并发导致的性能问题。

使用并发控制库

可以借助第三方库(如 p-limit)实现更灵活的并发控制。

const pLimit = require('p-limit');
const limit = pLimit(3);
const fetchData = async () => {
 const urls = ['https://api.example.com/data1', 'https://api.example.com/data2', 'https://api.example.com/data3'];
 const promises = urls.map(url => limit(() => fetch(url).then(res => res.json())));
 const results = await Promise.all(promises);
 console.log(results);
};
fetchData();

这种方式适合复杂场景,能够精细控制并发数量。

错误处理与重试机制

在并发请求中,可能会出现部分请求失败的情况。可以通过 Promise.allSettled 或重试机制提高成功率。

const fetchWithRetry = async (url, retries = 3) => {
 for (let i = 0; i < retries; i++) {
   try {
     const response = await fetch(url);
     if (!response.ok) throw new Error('请求失败');
     return await response.json();
   } catch (err) {
     if (i === retries - 1) throw err;
     await new Promise(resolve => setTimeout(resolve, 1000 * 2 ** i)); // 指数退避
   }
 }
};

注意事项

  1. 资源限制:合理设置并发数量,避免服务器过载或客户端性能下降。

  2. 错误处理:使用 Promise.allSettled 确保所有请求都被处理,无论成功与否。

  3. 安全性:确保请求通过安全渠道,并对敏感数据进行加密。

通过以上方法,可以根据实际需求灵活选择并发请求的实现方式,并结合错误处理和资源管理策略,提升前端性能和用户体验。