天天看点

Promise.all请求失败重发功能的实现

写爬虫时遇到用Promise.all同时请求多个页面,不可避免的会遇到某些请求失败的情况,这时可以实现一个“重发失败请求”的功能。

Promise.all(task).then().catch() 会在所有task都resolve时才会进then方法,并且把所有结果以一个数组返回。只要有一个失败,就会进catch。但如果在单个请求中定义了catch方法,就不会进Promise.all的catch方法。因此,可以在单个的catch中将失败的promise放入一个list,待一轮请求完成后,再去请求失败的请求。

let failedList = []

function getDataById (id) { // 这是单个请求
  return new Promise(function (resolve, reject) {
    getResponse(id, resolve, reject)
  }).catch(e => {
    failedList.push(getDataById (id)) // 如果失败,就重新发起请求,并将该请求放入failedList中以便后续处理
  })
}

function getResponse (id, resolve, reject) { // 模拟返回结果
  setTimeout(() => {
    if (Math.random() > 0.8) resolve({id, msg: 'ok'})
    else reject({id, msg: 'error'})
  }, 1000)
}


const RequestList = [getDataById(1), getDataById(2), getDataById(3)]

handlePromiseDone(RequestList)

let requestTime = 1 // 当前请求次数
let maxRequestTime = 5 // 最大重试次数
let result = [] // 最后的结果

function handlePromiseDone(requestList) { // 处理请求结果
  Promise.all(requestList).then(resolve => {
    result = result.concat(resolve.filter(i => i !== undefined)) // 过滤掉resolve列表里的失败请求的结果
    let failedLength = failedList.length
    if (failedLength > 0 && requestTime < maxRequestTime) { // 如果失败列表里有请求,并且请求次数不超过设定的值,就进行下一次请求
      console.log(`第${requestTime}次请求完成,其中成功${RequestList.length - failedLength}个,失败${failedLength}个,正在进行第${++requestTime}次请求...`)
      handlePromiseDone(failedList)
      failedList = [] // 清空本轮请求的failedList
    } else { // 表示所有请求都成功了,或者达到了最大请求次数。到这里就可以对result做进一步处理了。
      console.log(`请求完成,共请求${requestTime}次, 其中成功${RequestList.length - failedLength}个,失败${failedLength}个\n`, result)
    }
  }).catch(e => {
    console.log(e)
  })
}

           
Promise.all请求失败重发功能的实现