JS Async/Await 并行和串行发送请求的几种方法

1,059 阅读3分钟
开题

本文主要是列举一下使用js async/await 方法发送串行/并行请求的不同方法。以及容易错误的地方。

准备工作

首先我们先用Promise准备一个模拟发送请求函数 以及 一个urls数组。

const allRequests = [
  "http://jsonplaceholder.typicode.com/posts/1",
  "http://jsonplaceholder.typicode.com/posts/2",
  "http://jsonplaceholder.typicode.com/posts/3",
  "http://jsonplaceholder.typicode.com/posts/4",
  "http://jsonplaceholder.typicode.com/posts/5",
  "http://jsonplaceholder.typicode.com/posts/6",
];

function fetch(url){
  return new Promise((res, rej)=>{
    console.log('start fetching '+url)
    setTimeout(()=>{
      res(url);
      console.log('end fetching '+url)
    }, 2000) 
  })
}

function fetchError(url){
  return new Promise((res, rej)=>{
    console.log('start fetching '+url)
    setTimeout(()=>{
      rej("fetchError: "+ url);
      console.log('end fetching '+url)
    }, 2000) 
  })
}

//串行
async function serialFlow(){
  await fetch(allRequests[0]);
  await fetch(allRequests[1]);
}

serialFlow()
//得到的输出结果是:
//start fetching http://jsonplaceholder.typicode.com/posts/1
//等待。。。。
//end fetching http://jsonplaceholder.typicode.com/posts/1
//start fetching http://jsonplaceholder.typicode.com/posts/2
//等待。。。。
//end fetching http://jsonplaceholder.typicode.com/posts/2
//从这个结果可以看出第一个请求返回结果之后,第二个请求才开始。
//加入第一个请求需要等待一个小时才返回结果,那么第二个请求就等一个小时之后才发送。
//如果第一个请求永远没有返回,那么第二个请求就不会发出。
//我们可以通过修改setTimeout里面的等待时间或者去掉promise中的res(url)来自己验证。

然后我们再来看一下并行。针对上面串行的例子,我们换一种写法便可以实现并行。

async function parallelFlow(){
  const res1 = fetch(1);
  const res2 = fetch(2);
}
parallelFlow();
//返回的结果如下:
//start fetching 1
//start fetching 2
//end fetching 2
//end fetching 1

但是如果我们要对fetch的返回结果进行处理应该怎么做呢?如果我们在parallelFlow打印res1我们会发现返回的是 Promise { },因此我们需要将Promise的状态改为resolved。可以这样做。

async function parallelFlow(){
  const res1 = fetch(allRequests[0]);
  const res2 = fetch(allRequests[1]);
  const r1 = await res1;
  const r2 = await res2;
  console.log(r1 +" "+ r2)
}
   

现在看起来似乎实现了我们想要的简单并行发送请求,但是这里需要注意的点是,如果请求有错误的时候的处理。

async function errorHandlingParalle(){
   try{
      fetchError(allRequests[0])
    }catch(e){
      console.error(e)
    }
}

async function errorHandlingSequence(){
   try{
      await fetchError(allRequests[0])
    }catch(e){
      console.error(e)
    }
}

errorHandlingParalle()
errorHandlingSequence()
// 我们可以发现在errorHandlingSequence捕捉到了错误理由,
// 虽然我们在errorHandlingParalle里面加了 try catch 块,但是错误却并没有被捕捉到。  

为了避免以上并行请求无法捕捉错误的情况发生,我们可以改造一下上面的代码

async function request(url){
  try{
    const res = await fetchError(url);
    return res;
  }catch(e){
    console.error('request error '+ e)
  }
}

async function errorHandlingParalle(){
  try{
    request(allRequests[0]);
    request(allRequests[1]);
  }catch(e){
    console.error('err ',e)
  }
}
// 或者如果不需要在 errorHandlingParalle 显示新的错误信息的话。也可以直接去掉try catch block
async function errorHandling(){
  request(allRequests[0]);
  request(allRequests[1]);
}

errorHandlingParalle()
/* 结果
start fetching http://jsonplaceholder.typicode.com/posts/1
start fetching http://jsonplaceholder.typicode.com/posts/2
end fetching http://jsonplaceholder.typicode.com/posts/1
request error fetchError: http://jsonplaceholder.typicode.com/posts/1
end fetching http://jsonplaceholder.typicode.com/posts/2
request error fetchError: http://jsonplaceholder.typicode.com/posts/2
*/
话外

串行也可以使用递归来实现。如下

async function requestRecursive(index){
  if(index >= allRequests.length) return;
  try{
    await fetch(allRequests[index]);
    requestRecursive(++index);
  }catch(e){
    console.error('err ',e)
  }
}

但是这种递归的写法的特点是,如果任意一个请求返回有错误,进入catch里面就会停止继续递归发送请求。 因此可以改写成

async function requestRecursive(index){
  if(index >= allRequests.length) return;
  try{
    await fetchError(allRequests[index]);
  }catch(e){
    console.error('err ',e)
  }
  requestRecursive(++index);
}
    
//或者可以同上面的并行模式的处理方式,如果不需要在显示新的错误信息的话。也可以直接去掉try catch block改成
async function requestRecursive(index){
  if(index >= allRequests.length) return;
  await request(allRequests[index]);
  requestRecursive(++index);
}