使用Promise控制请求并发数的有效方法

引言

在现代Web开发中,与服务器进行异步通信是常见的需求。然而,当需要同时发送多个请求时,如果不加以控制,可能会导致服务器负载过高,影响应用性能。本文将介绍如何使用Promise来控制请求的并发数,以确保请求能够有序地发送和处理,提高应用的效率和稳定性。

并发控制的意义

首先,异步编程是为了解决程序中耗时操作的执行问题。举个例子,假设你需要同时发送多个请求给服务器,等待它们都返回结果后再继续处理,但又不希望这些请求一个接一个地按顺序执行,因为这样会浪费时间。这时,我们可以利用Promise来控制请求的并发数,以提高效率。

Promise可以看作是一种表示异步操作结果的对象。它有两个重要的状态:进行中(pending)和已完成(fulfilled)或已拒绝(rejected)。当我们发送一个请求时,会创建一个对应的Promise对象,这个对象可以用来监听请求的结果。我们可以通过调用Promise的then方法,传入一个回调函数,在请求完成后执行相应的处理逻辑。

为了控制请求的并发数,我们可以设置一个计数器来记录当前正在进行的请求数量。当计数器小于并发数限制时,我们可以发送新的请求,计数器加1。当计数器达到并发数限制时,我们暂停发送新的请求,等待其中之一的请求完成,然后再继续发送下一个请求。这样就能保证同时发送的请求数量不超过设定的限制。

通过这种方式,我们可以合理地控制请求的并发数,避免对服务器造成过大的负荷,同时提高程序的执行效率。

总结起来,掌握Promise和异步编程的关键在于理解如何使用Promise对象来管理异步操作,利用计数器来控制并发数。这样我们就能更好地处理多个请求,提高应用的性能和稳定性。

深入理解并发, 并行,并发控制, 切片控制

并发(Concurrency)和并行(Parallelism)是计算机领域中常用的两个概念。

并发是指多个任务在同一个时间段内交替执行,它强调任务之间的交替执行和共享资源的竞争。在并发执行中,多个任务交替执行的顺序是不确定的,每个任务执行一段时间后,切换到下一个任务,通过快速的切换使得任务之间产生了一种同时进行的错觉。并发可以提高系统的吞吐量和响应性,但并不一定能够加速单个任务的执行速度。

并行是指多个任务同时进行,每个任务在不同的处理单元(如多个CPU核心)上执行。并行执行的任务之间相互独立,彼此之间不会产生竞争或依赖关系。通过并行执行,可以提高整体任务的处理能力,加速任务的执行速度。

并发控制(Concurrency Control)是指在并发执行的情况下,保证多个任务对共享资源的访问是正确和有序的。在并发环境中,多个任务可能同时访问和修改共享资源,如果不进行适当的控制,就会产生数据不一致、竞争条件和死锁等问题。并发控制的目标是通过采用合适的技术和策略,保证在多个任务同时执行时,对共享资源的访问和修改是安全和可靠的。

切片控制(Scheduling Control)是一种并发控制的策略,用于控制并发执行的任务按照一定的顺序和时间片长度进行切换。在多任务并发执行的情况下,切片控制决定了每个任务执行的时间片长度和切换的时机。通过合理的切片控制,可以平衡多个任务的执行,避免某个任务长时间占用资源而导致其他任务无法执行的情况。

切片控制通常涉及调度算法和调度器的设计和实现。调度算法决定了任务切换的策略,如先来先服务(First-Come, First-Served)、轮转(Round-Robin)、优先级调度等。调度器负责根据调度算法的策略,实现任务的切换和执行管理。

综上所述,并发和并行是不同的概念,而并发控制和切片控制则是在并发执行环境中保证任务执行的安全性和顺序性的技术手段。了解并理解这些概念对于处理多任务并发执行的场景非常重要。

引出问题

// 设计一个函数,可以限制请求的并发,同时请求结束之后,调用callback函数
// sendRequest(requestList:,limits,callback)
sendRequest(

[()=>request('1'),

()=>request('2'),

()=>request('3'),

()=>request('4')],

3, //并发数

(res)=>{

    console.log(res)

})

// 其中request 可以是: 
function request (url,time=1){

    return new Promise((resolve,reject)=>{

        setTimeout(()=>{

            console.log('请求结束:'+url);

            if(Math.random() > 0.5){

                resolve('成功')

            }else{

                reject('错误;')

            }

        },time*1e3)

    })
}

实现思路

为了实现请求的并发数控制,我们可以利用Promise和JavaScript的异步特性。下面是实现的步骤:

  1. 创建一个请求队列,用于存储待发送的请求。
  2. 设置一个并发数限制,即同时处理的最大请求数量。
  3. 使用一个计数器来记录当前正在处理的请求数量,初始值为0。
  4. 遍历请求队列,对每个请求执行以下步骤: a. 如果计数器小于并发数限制,将计数器加1,并发送该请求。 b. 如果计数器等于并发数限制,使用Promise.race方法等待任一请求完成。 c. 当有请求完成时,将计数器减1,并从请求队列中移除该请求。
  5. 重复步骤4,直到所有请求完成。

实现代码( 基础版 )

async function sendRequest(requestList, limits, callback) {
  const processRequest = async (request) => {
    // 执行请求的逻辑,这里使用console.log语句作为示例
    console.log("Sending request:", request);
    // 模拟请求的异步执行
    await new Promise((resolve) => setTimeout(resolve, 1000));
    // 请求完成后调用回调函数
    callback(request);
  };

  // 创建一个计数器,用于限制并发请求数量
  let counter = 0;

  // 并发控制函数
  const controlConcurrency = async () => {
    while (counter < limits && requestList.length > 0) {
      const request = requestList.shift(); // 取出下一个请求
      counter++;
      await processRequest(request); // 执行请求
      counter--;
    }

    // 检查是否还有未完成的请求
    if (counter === 0 && requestList.length === 0) {
      callback(); // 所有请求完成后调用回调函数
    }
  };

  // 启动并发控制
  for (let i = 0; i < limits; i++) {
    controlConcurrency();
  }
}


// 请求列表
const requestList = ["request1", "request2", "request3", "request4", "request5"];

// 并发限制数
const limits = 2;

// 回调函数
const callback = (request) => {
  console.log("Request completed:", request);
};

// 发送请求
sendRequest(requestList, limits, callback);

优化方案

  1. 使用Promise.race方法:与Promise.all相反,Promise.race方法可以在一组请求中任何一个请求完成时就触发回调函数,这可以用于实现更实时的响应。可以在控制并发请求的循环中使用Promise.race来等待最快完成的请求,并处理其结果。
  2. 采用连接池:创建一个连接池来管理请求的连接资源,而不是为每个请求都创建新的连接。这可以减少连接的创建和销毁开销,提高请求的效率。连接池可以维护一个可用连接的队列,并在需要时分配连接给请求。
  3. 实现请求队列:将请求添加到队列中,并按照一定的策略(如先进先出)进行处理。在控制并发请求的过程中,从队列中取出请求并执行,以保持请求的顺序性。这样可以避免同时发送大量请求,减少服务器负载。
  4. 使用缓存:对于重复的请求,可以使用缓存来存储已经获取的结果,以减少不必要的请求。在发起新请求之前,先检查缓存中是否存在相应的结果,如果存在,则直接使用缓存的结果,而不需要进行实际的网络请求。
  5. 并发级别调优:根据实际情况,调整并发请求数量的限制。如果网络环境较差或服务器端对并发请求数量有限制,可以适当降低并发级别,避免造成请求失败或被服务器拒绝的情况。

请注意,每个优化方式的适用性取决于具体的使用场景和要求。根据实际情况选择适合的优化方式,可以有效提升代码的性能和用户体验。

1.采用Promise.race方法优化

function sendRequest(requestList, limits, callback) {
  const processRequest = async (request) => {
    console.log("Sending request:", request);
    await new Promise((resolve) => setTimeout(resolve, 1000));
    return request;
  };

  const requests = requestList.map((request) => processRequest(request));

  const sendLimitedRequests = async () => {
    while (requests.length > 0) {
      const limitedRequests = requests.splice(0, limits);
      const fastestPromise = Promise.race(limitedRequests);
      const result = await fastestPromise;
      callback(result); // 请求完成后调用回调函数
    }
    callback(); // 所有请求完成后调用回调函数
  };

  sendLimitedRequests().catch((error) => {
    console.error("Error occurred:", error);
  });
}

代码解释:

  1. 定义了一个异步函数 processRequest,用于执行单个请求的逻辑。在这个示例中,使用 console.log 来模拟实际的请求,然后使用 setTimeout 来模拟异步请求的延迟,并返回请求本身作为结果。
  2. 将所有请求通过 map 方法映射为执行请求的 Promise,并将这些 Promise 存储在 requests 数组中。
  3. 定义了一个异步函数 sendLimitedRequests,用于控制并发请求的数量。
  4. 在一个循环中,检查 requests 数组是否还有待处理的请求。
  5. 使用 splice 方法从 requests 数组中截取 limits 个请求,形成一个新的数组 limitedRequests,表示当前限制的请求数量。
  6. 使用 Promise.race 方法,传入 limitedRequests 数组,返回最先完成的 Promise(即最快完成的请求)。
  7. 使用 await 等待最快完成的请求的结果,并将结果保存在 result 变量中。
  8. 调用回调函数 callback,将请求的结果传递给回调函数。
  9. 重复步骤 4 到步骤 8,直到 requests 数组中的所有请求都被处理完毕。
  10. 当所有请求完成后,调用回调函数 callback,此时传递的参数为空,表示所有请求都已完成。
  11. 在调用 sendLimitedRequests 函数时,使用 catch 方法捕获可能发生的错误,并将错误信息打印到控制台。

总结来说,这段代码通过限制并发请求数量,并利用 Promise.race 方法等待最快完成的请求,实现了对一组请求的控制和处理,并在请求完成后调用回调函数进行进一步的处理

2.采用连接池

// 假设已有一个连接池对象 connectionPool,包含了可用连接的队列

function sendRequest(requestList, limits, callback) {
  const processRequest = async (request) => {
    console.log("Sending request:", request);
    await new Promise((resolve) => setTimeout(resolve, 1000));
    return request;
  };

  const sendLimitedRequests = async () => {
    while (requestList.length > 0) {
      const limitedRequests = requestList.splice(0, limits);
      const promises = limitedRequests.map((request) => processRequest(request));
      const results = await Promise.all(promises);
      results.forEach((result) => {
        callback(result); // 请求完成后调用回调函数
      });
    }
    callback(); // 所有请求完成后调用回调函数
  };

  sendLimitedRequests().catch((error) => {
    console.error("Error occurred:", error);
  });
}

代码解释:

  1. 定义了一个异步函数 processRequest,用于执行单个请求的逻辑。在这个示例中,使用 console.log 来模拟实际的请求,然后使用 setTimeout 来模拟异步请求的延迟,并返回请求本身作为结果。
  2. 定义了一个异步函数 sendLimitedRequests,用于控制并发请求的数量。
  3. 在一个循环中,检查 requestList 数组是否还有待处理的请求。
  4. 使用 splice 方法从 requestList 数组中截取 limits 个请求,形成一个新的数组 limitedRequests,表示当前限制的请求数量。
  5. 使用 map 方法遍历 limitedRequests 数组,将每个请求传入 processRequest 函数中执行,返回一个 Promise 数组 promises
  6. 使用 Promise.all 方法,传入 promises 数组,等待所有请求的 Promise 都完成,并将它们的结果保存在 results 数组中。
  7. 使用 forEach 方法遍历 results 数组,对每个请求的结果调用回调函数 callback
  8. 重复步骤 3 到步骤 7,直到 requestList 数组中的所有请求都被处理完毕。
  9. 当所有请求完成后,调用回调函数 callback,此时传递的参数为空,表示所有请求都已完成。
  10. 在调用 sendLimitedRequests 函数时,使用 catch 方法捕获可能发生的错误,并将错误信息打印到控制台。

3.实现请求队列

function sendRequest(requestList, limits, callback) {
  const processRequest = async (request) => {
    console.log("Sending request:", request);
    await new Promise((resolve) => setTimeout(resolve, 1000));
    return request;
  };

  const requestQueue = [...requestList];

  const sendNextRequest = async () => {
    if (requestQueue.length === 0) {
      callback(); // 所有请求完成后调用回调函数
      return;
    }

    const request = requestQueue.shift();
    try {
      const result = await processRequest(request);
      callback(result); // 请求完成后调用回调函数
    } catch (error) {
      console.error("Error occurred:", error);
    } finally {
      sendNextRequest(); // 处理下一个请求
    }
  };

  for (let i = 0; i < limits; i++) {
    sendNextRequest(); // 启动请求队列处理
  }
}

代码解释:

  1. 定义了一个异步函数 processRequest,用于执行单个请求的逻辑。在这个示例中,使用 console.log 来模拟实际的请求,然后使用 setTimeout 来模拟异步请求的延迟,并返回请求本身作为结果。
  2. 创建了一个请求队列 requestQueue,使用展开运算符 ...requestList 数组复制到请求队列中。
  3. 定义了一个异步函数 sendNextRequest,用于处理下一个请求。
  4. 检查 requestQueue 数组的长度,如果长度为 0,则表示所有请求已完成,调用回调函数 callback,并返回。
  5. 使用 shift 方法从 requestQueue 中取出下一个请求。
  6. 尝试执行请求的逻辑,并使用 await 等待请求的完成。
  7. 如果请求成功完成,调用回调函数 callback,将请求结果传递给回调函数。
  8. 如果请求过程中发生错误,使用 catch 捕获错误并打印错误信息。
  9. 无论请求成功还是失败,使用 finally 关键字确保 sendNextRequest 函数递归调用,处理下一个请求。
  10. 使用一个 for 循环,根据 limits 参数的值,多次调用 sendNextRequest 函数,启动请求队列的处理。

总结来说,这段代码通过维护一个请求队列和递归调用的方式,实现了限制并发请求数量,并在请求完成后调用回调函数进行进一步的处理。它将请求的处理过程分散在多个异步函数中,并使用递归调用来处理下一个请求,直到所有请求都完成。

4.使用缓存

const requestCache = new Map();

function sendRequest(requestList, limits, callback) {
  const processRequest = async (request) => {
    console.log("Sending request:", request);

    // 检查缓存中是否存在请求结果
    if (requestCache.has(request)) {
      console.log("Using cached result for request:", request);
      return requestCache.get(request);
    }

    // 执行请求的逻辑
    const result = await new Promise((resolve) => setTimeout(() => {
      resolve(request);
    }, 1000));

    // 将请求结果存入缓存
    requestCache.set(request, result);

    return result;
  };

  const sendLimitedRequests = async () => {
    while (requestList.length > 0) {
      const limitedRequests = requestList.splice(0, limits);
      const promises = limitedRequests.map(processRequest);
      const results = await Promise.all(promises);
      results.forEach((result) => {
        callback(result); // 请求完成后调用回调函数
      });
    }
    callback(); // 所有请求完成后调用回调函数
  };

  sendLimitedRequests().catch((error) => {
    console.error("Error occurred:", error);
  });
}

代码解释:

  1. 创建了一个 Map 对象 requestCache 用于缓存请求的结果。
  2. 定义了一个异步函数 processRequest,用于执行单个请求的逻辑。
  3. processRequest 函数中,首先检查缓存中是否已存在请求结果。如果缓存中存在请求结果,则直接返回缓存中的结果。
  4. 如果缓存中不存在请求结果,则执行实际的请求逻辑。在这个示例中,使用 setTimeout 模拟异步请求,并在 1 秒后将请求本身作为结果返回。
  5. 将请求结果存入缓存 requestCache 中,使用请求作为键,结果作为值。
  6. 在主函数 sendRequest 中,继续使用 sendLimitedRequests 函数来控制并发请求数量。
  7. 在循环中,先检查 requestList 数组是否还有待处理的请求。
  8. 使用 splice 方法从 requestList 数组中截取 limits 个请求,形成一个新的数组 limitedRequests,表示当前限制的请求数量。
  9. 使用 map 方法遍历 limitedRequests 数组,将每个请求传入 processRequest 函数中执行,返回一个 Promise 数组 promises
  10. 使用 Promise.all 方法,传入 promises 数组,等待所有请求的 Promise 都完成,并将它们的结果保存在 results 数组中。
  11. 使用 forEach 方法遍历 results 数组,对每个请求的结果调用回调函数 callback
  12. 重复步骤 7 到步骤 11,直到 requestList 数组中的所有请求都被处理完毕。
  13. 当所有请求完成后,调用回调函数 callback,此时传递的参数为空,表示所有请求都已完成。
  14. 在调用 sendLimitedRequests 函数时,使用 catch 方法捕获可能发生的错误,并将错误信息打印到控制台。

总结来说,这段代码在前面的基础上添加了请求结果的缓存功能,以减少重复请求,并在请求开始前先检查缓存中是否存在结果。这样可以提高性能和效率,特别是在请求重复发送的场景下。

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 好的,以下是一个简单的控制请求并发数的js函数: ```javascript function limitRequestConcurrency(urls, maxConcurrency, callback) { let activeRequests = ; let index = ; function next() { if (index === urls.length && activeRequests === ) { return; } while (activeRequests < maxConcurrency && index < urls.length) { const url = urls[index++]; activeRequests++; fetch(url) .then(response => response.json()) .then(data => { callback(data); }) .catch(error => { console.error(error); }) .finally(() => { activeRequests--; next(); }); } } next(); } ``` 这个函数接受三个参数: - `urls`:一个包含请求URL的数组。 - `maxConcurrency`:最大并发请求数。 - `callback`:请求成功后的回调函数,接受一个参数,即请求返回的数据。 函数内部使用了递归调用来控制并发数,当活跃请求数小于最大并发数时,就会发起新的请求,否则等待已有请求完成后再发起新的请求。 ### 回答2: 可以使用以下的JavaScript函数来控制请求并发数: ```javascript function controlConcurrency(requests, maxConcurrent) { const results = []; let currentConcurrent = 0; let i = 0; function handleRequest() { if (i >= requests.length) { // 所有请求处理完毕 return; } if (currentConcurrent >= maxConcurrent) { // 达到最大并发数,等待一段时间再处理下一个请求 setTimeout(handleRequest, 100); return; } // 处理请求 const request = requests[i]; const promise = request(); currentConcurrent++; promise .then(response => { // 处理请求结果 results.push(response); }) .finally(() => { // 无论请求成功或失败,都将并发数减1,然后处理下一个请求 currentConcurrent--; i++; handleRequest(); }); } // 开始处理请求 handleRequest(); // 返回Promise对象,当所有请求处理完毕时,将结果数组作为resolve参数 return new Promise(resolve => { setTimeout(() => { if (i >= requests.length && currentConcurrent === 0) { resolve(results); } }, 1000); // 超时时间,避免请求处理时间过长导致无法结束Promise }); } ``` 这个函数接收两个参数:`requests`和`maxConcurrent`。`requests`是一个包含多个请求函数的数组,每个请求函数应该返回一个Promise对象。`maxConcurrent`表示最大的并发数。 函数会按照最大并发数依次处理请求,当达到最大并发数时,会等待一段时间再继续处理下一个请求。处理请求的过程使用递归实现,直到所有请求都处理完毕。 函数返回一个Promise对象,在所有请求处理完毕时,将结果数组作为resolve参数传递。你可以使用`.then()`方法来处理最终的结果。 ### 回答3: 当需要控制请求并发数时,可以使用以下的 JavaScript 函数来实现: ```javascript function requestControlledConcurrency(urls, maxConcurrentRequests) { return new Promise((resolve, reject) => { let currentIndex = 0; let activeRequests = 0; let completedRequests = 0; let results = []; function makeRequest(url) { activeRequests++; return fetch(url) .then(response => { activeRequests--; completedRequests++; results.push(response); if (completedRequests === urls.length) { resolve(results); } else { processNextRequest(); } }) .catch(error => { reject(error); }); } function processNextRequest() { while (activeRequests < maxConcurrentRequests && currentIndex < urls.length) { makeRequest(urls[currentIndex]); currentIndex++; } } processNextRequest(); }); } ``` 这个函数接受两个参数:`urls` 是一个包含多个请求 URL 的数组,`maxConcurrentRequests` 是允许的最大并发请求数。函数会返回一个 Promise 对象,成功时返回包含所有请求结果的数组,失败时返回错误信息。 这个函数会依次发起请求,同时保持活动请求数不超过设定的最大并发请求数。在每次请求完成后,会判断是否还有未完成的请求,如果有则继续发起新的请求。只有当所有请求都完成后,Promise 才会被解析并返回结果数组。 可以按照以下示例使用这个函数: ```javascript const urls = ['url1', 'url2', 'url3', 'url4', 'url5']; const maxConcurrentRequests = 2; requestControlledConcurrency(urls, maxConcurrentRequests) .then(results => { console.log('所有请求完成'); console.log(results); }) .catch(error => { console.error('请求失败'); console.error(error); }); ``` 这个示例中,将同时发起两个请求,并且保持同时进行的请求不超过两个,直到所有请求完成。完成后,将会打印出所有请求的结果数组。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Double-C

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值