Promise.all处理多次reject/最多n次reject

4,225 阅读3分钟
原文链接: zhuanlan.zhihu.com

首先,抛出我们的需求:

实现一个函数,在多次异步请求全部结束之后再进行处理,即使有一次或多次失败,在失败时我们可以处理这些失败的情况,但是不会阻塞其它请求

我们首先想到的是 Promise.all:

function settle (promises) {
  return Promise.all(promises)
  .then(res => {})
  .catch(err => {}) // 若其中的一个失败则会走catch
}

然鹅,Promise.all()其中的一个请求失败了就会走catch,此时无法满足请求全部结束的需求。

但是反过来想Promise.all()是需要所有的都成功才不会走catch,这样才不会影响处理其它请求的响应,这样我们就想,能不能让所有的promises都走resolve,把error的信息扔到resolve中。基于这个思路可以把所有的promise转成resolve,首先我们看要怎样将一个promise都转成resolve的处理:

const p2 = new Promise((res, reject) => {
  setTimeout(() =>{
    reject(2)
  }, 2000)
})

let resolvePromise = p2.then(res => res).catch(err => err)

resolvePromise.then(res => {
  console.log('res', res)
}).catch(err => {
  console.log('err', err)
})
//输出 resolve 2

处理了单个,那么多个也是类似的,再结合Promise.all

let transferedPromises = (promises) => { // 返回一个处理之后的promise数组
  return promises.map(promise => {
    return promise.then(res => res).catch(err => err)
  })
}
let promiseArr = transferedPromises(promises)
Promise.all(promiseArr).then(resArr => {
  console.log(resArr)
})

让我们验证一下:

const p1 = new Promise((res, rej) => {
  setTimeout(() =>{
    res(1)
  }, 1000)
})
const p2 = new Promise((res, rej) => {
  setTimeout(() =>{
    rej('err 2')
  }, 2000)
})
const p3 = new Promise((res, rej) => {
  setTimeout(() =>{
    res(3)
  }, 3000)
})
let promises = [p1, p2, p3]


let transferedPromises = (promises) => { // 返回一个处理之后的promise数组
  return promises.map(promise => {
    return promise.then(res => res).catch(err => err)
  })
}
let promiseArr = transferedPromises(promises)
Promise.all(promiseArr).then(resArr => {
  console.log(resArr)
})
// 输出 [ 1, 'err 2', 3 ]

至此我们实现了开始的需求,如果需要其它更多的信息,可以在then和catch的hanler中增加一些自己想要的,比如:

let thenHandler = function (res) {
  return {
    res: res,
    status: 'success'
  }
}
let catchHandler = function (err) {
  return {
    err: err,
    status: 'failed'
  }
}
let transferedPromises = (promises) => { // 返回一个处理之后的promise数组
  return promises.map(promise => {
    return promise.then(thenHandler).catch(catchHandler)
  })
}

好,我们处理了多次reject的情况,每次reject都可以拿得到reject具体的内容,再根据这些内容做些失败的提示了什么的。

然后,来了个需求:5(n)次异步请求最多允许失败2(m,m < n)次.

这个需求我们依然可以利用Promise.all()需要所有的promise都是resolve状态这个特性:在超过2(m, m < n)次时返回一个reject状态的promise,改造catchHandler:

let count = 2 // 最多失败次数
let errorCount = 0 // 失败的次数

let catchHandler = function (err) {
    errorCount++
    if (errorCount >= count) {
      return Promise.reject(`catch 超过了${count}次`) // reject的内容可以自定义
    } else {
      return {
        err: err,
        status: 'failed'
      }
    }
  }

整体扔到一个函数里就是:

const dealPromises = (promises, count) => {
  let errorCount = 0
  let thenHandler = function (res) {
    return {
      res: res,
      status: 'success'
    }
  }
  let catchHandler = function (err) {
    errorCount++
    if (errorCount >= count) {
      return Promise.reject(`catch 超过了${count}次`)
    } else {
      return {
        err: err,
        status: 'failed'
      }
    }
  }
  let transferedPromises = (promises) => { // 返回一个处理之后的promise数组
    return promises.map(promise => {
      return promise.then(thenHandler).catch(catchHandler)
    })
  }
  let promiseArr = transferedPromises(promises)
  Promise.all(promiseArr).then(resArr => {
    console.log('来到了then', resArr)
  }).catch(err => {
    console.log('来到了catch', err)
  })
}
dealPromises(promises, 3)
// 输出: 来到了catch catch 超过了3次
dealPromises(promises, 10)
// 输出: 来到了then [ { res: 1, status: 'success' },
  // { err: 'err 2', status: 'failed' },
  // { res: 3, status: 'success' },
  // { err: 'err 4', status: 'failed' },
  // { err: 'err 5', status: 'failed' },
  // { res: 4, status: 'success' },
  // { err: 'err 7', status: 'failed' } ]