支线任务遇到的怪——并发请求

并发请求指的是在同一段时间内,永远有一个或多个请求被发送出去的情况。并不是指这些请求完全同时发送,而是一段时间内,这些请求可能因为时间的微小差异而先后被发送。这些请求可能彼此相互独立,也可能存在相互依赖的关系

并发请求的一个典型应用场景是网页加载。当你访问一个网页时,浏览器会同时向服务器发送多个请求,来获取页面的HTML文档、样式表、JavaScript脚本、图片等多种资源。这些请求基本上是同时发出的,但由于网络延迟和服务器处理速度的差异,这些请求可能会在不同的时间点得到响应。

在处理并发请求时,我们通常要考虑以下问题:

  • 请求的顺序性:如果一个请求的处理结果依赖于另一个请求的结果,那么我们就需要确保这两个请求的执行顺序。

  • 请求的并发数:如果有太多的请求同时发送,可能会对服务器造成负担,也可能会受到浏览器或服务器对并发连接数的限制。

在JavaScript中,我们可以使用Promiseasync/await等方式来管理和控制并发请求。对于超出限制的并发请求,我们还可以使用一些第三方库,如axios、request-promise等来进行队列化处理或并发控制。

在JavaScript中,有很多不同的方法可以用来处理并发请求:

  1. Promise.all():这是解决并行请求处理的一个常用方法。可以将一个需要并发执行的请求数组作为参数传递给Promise.all()方法,这些请求会并发地执行,而Promise.all()会返回一个新的Promise对象,只有当所有请求都成功执行并返回响应后,这个新的Promise才会变为resolved状态

例如:

let request1 = fetch('https://api.myjson.com/bins/9hog6');
let request2 = fetch('https://api.myjson.com/bins/nttn4');

Promise.all([request1, request2])
.then(responses => {
  return Promise.all(responses.map(response => response.json()))
})
.then(data => console.log(data))
.catch(err => console.log(err));
  1. Async/Await:也可以使用async函数和await表达式来处理并发请求。在async函数内部,你可以使用await来暂停函数执行,直到Promise被resolved或rejected为止。

例如:

async function fetchAndDecode(url) {
  let response = await fetch(url);
  let data = await response.json();
  return data;
}

(async function() {
  let data1 = await fetchAndDecode('https://api.myjson.com/bins/wx4io');
  let data2 = await fetchAndDecode('https://api.myjson.com/bins/jv265');
  console.log(data1, data2);
})();
  1. Promise.any()/Promise.race():这两个方法都会在任何一个Promise达到resolved状态时结束,但有区别,Promise.race()在任一Promise变为fulfilled或reject时结束,Promise.any()则会在任一Promise达到fulfilled状态时结束

怎么去保证并发请求的顺序性?

在处理并发请求时,如果需要保证请求的顺序性,则通常意味着请求之间存在依赖关系,可能需要先发送某个请求,并等待接收和处理其响应之后,再发送下一个请求。

在JavaScript中,有多种方式可以实现这种顺序执行的效果。

  1. 使用Promise的链式调用:通过在.then()的回调函数中返回一个新的Promise,就可以形成一个Promise链,从而使多个操作能够按照指定的顺序执行。例如:
fetch('https://api.example.com/data1')
  .then(response => response.json())
  .then(data1 => {
    console.log(data1);
    return fetch('https://api.example.com/data2');
  })
  .then(response => response.json())
  .then(data2 => console.log(data2));

在上面的代码中,第二个fetch请求会在第一个fetch请求成功并处理完毕后发出。

  1. 使用async/awaitasync/await是基于Promise的语法糖使异步操作看起来更像是同步操作,以此可以很容易地实现请求的顺序性。例如:
async function fetchData() {
  let response1 = await fetch('https://api.example.com/data1');
  let data1 = await response1.json();
  console.log(data1);
  
  let response2 = await fetch('https://api.example.com/data2');
  let data2 = await response2.json();
  console.log(data2);
}

fetchData();

在上面的代码中,使用了async/await确保第二个fetch请求会在第一个fetch请求成功并处理完毕后发出。

注意:以上二种方法都有一个共同的特点,那就是后一个请求必须等待前一个请求完全结束后才能开始。如果你的请求之间没有依赖关系,那么这种做法可能会降低程序的性能,因为不能充分利用网络的并发性。在没有依赖关系的情况下,更好的做法是同时发出这些请求,然后在所有请求都结束后再进行后续处理(可以使用Promise.all()

怎么控制请求的并发数?

在 JavaScript 中,并发请求的并发数是由浏览器自己决定的,这一数值通常是固定的,通常为6-8。然而,当我们构建后端应用程序,或者需要控制并发请求数量时

以下是一种常见的控制并发请求并发数的方法:

我们可以使用队列的方法来控制并发数量。可以创建一个最大并发数固定的请求队列,当一个请求完成时,再从队列里取出下一个请求,这样就可以控制并发数不超过设定的最大并发数。

let maxConcurrentRequests = 5; // 设定最大并发数
let currentRequests = 0; // 当前并发数
let requestQueue = []; // 请求队列

function request(url){
  //如果当前并发请求数(`currentRequests`)小于最大并发数(`maxConcurrentRequests`),
  //函数内部会调用fetch来发送一个请求,并把当前并发数加一。Fetch返回的是一个Promise,请求完
  //成后会执行`then()`里面的内容,把当前请求数减一,然后检查请求队列(`requestQueue`),
  //如果队列中还有请求,取出队列中的第一个请求并调用`request`函数发送
  // 如果当前并发请求数达到了最大并发数,那么新的请求
  //就不能被立即执行,于是把新的请求放入请求队列中等待
  if(currentRequests < maxConcurrentRequests){
    currentRequests++;
    fetch(url).then(() => {
      currentRequests--; // 请求结束,减少并发数
      if (requestQueue.length > 0) { 
        // 如果队列中还有等待的请求,就取出一个继续发送
        request(requestQueue.shift()); 
      }
    });  
  } else {
    requestQueue.push(url);
  }
}

// 以下是对以上函数的使用方式,模拟发送并发请求
for(let i=0; i<15; i++){
  request('https://someurl.com/api');
}

当并发数超过指定的阈值时,新的请求会被放置在队列中,待一个请求完成将请求结束返回之后再从队列中取出新的请求执行。这样,任何时候实际发出的请求数量都不会超过我们预设的上限。
很多第三方的包/库提供了并发控制的功能,如 async.jsbottleneckp-limit

你可能感兴趣的:(升级记,javascript,开发语言,前端)