Skip to main content

实现 Promise 的失败重试

JS 组件 Promise 没有提供重试功能,因为一般前端开发不能容错,出错了要立即反馈给用户,让用户做选择。

重试处理一般用在硬件交互上,比如蓝牙、Wi-Fi、串口等。有时候它们可能会因为信号强度不够,出现失真之类的错误,这时需要像TCP那样,自己实现一套重发(重试)机制。

Snippet

withRetry 执行多次重试,返回 Promise。

  • 如果执行中遇到 resolve 则停止重试并立即返回结果。

  • 如果执行中遇到 reject 则准备下一次重试。

  • 可以在执行过程用 stopRetry 停止之后的重试

/**
 * @brief 执行重试,遇到 resolve 立即结束重试过程
 * 
 * @param {number} retry 重试次数,至少 1
 * 
 * @param {(stopRetry, currentRetry) => Promise} 执行方法
 * @argument {stopRetry} 可以通过 stopRetry 停止之后的重试
 * @argument {currentRetry} 当前重试次数,从 0 开始
 * 
 * @returns Promise
 */
export default function withRetry<T = unknown>(
  retry = 2,
  fn: (stopRetry: () => void, currentRetry: number) => Promise<T>
) {
  retry = Math.max(1, retry)
  return new Promise<T>((resolve, reject) => {
    let i = 0
    let end = false,
      lastErr = null
    const action = () => {
      if (i < retry && !end) {
        fn(() => (end = true), i + 1)
          .then(resolve)
          .catch((err) => {
            i++
            lastErr = err
            action()
          })
      } else {
        reject(lastErr)
      }
    }
    action()
  })
}

Example

读资源,最多重试 5 次

withRetry(5, () => Promise((resolve, reject) => {
  readResource('log.txt')
    .then(content => resolve(content))
    .catch(err => reject(err))
}))
.then(content => {
  // resolve 传递的 content
})
.catch(err => {
  // reject 传递最后一次的 err
})

读资源,最多重试 5 次,遇到超时则停止重试

withRetry(5, (stopRetry) => Promise((resolve, reject) => {
  readResource('log.txt')
    .then(content => resolve(content))
    .catch(err => {
      if (err === 'timeout') stopRetry();
      reject(err)
    })
}))
.then(...)
.catch(...)

读资源,最多重试 5 次,打印当前重试次数

withRetry(5, (stopRetry, currentRetry) => Promise((resolve, reject) => {
  console.log(`当前重试: ${currentRetry}`); // 重试次数从 0 开始
  readResource('log.txt')
    .then(content => resolve(content))
    .catch(err => reject(err))
}))
.then(...)
.catch(...)