假如有这样一个场景,有上万条数据,需要分100条一批的分批次并行调用接口,假如接口返回失败,提示该批次失败并于2秒后重试, 假如重试又失败,提示4秒后重试,如果还是失败则8秒后重试,当重试3次后依然失败那么停止重试。 使用RxJS 简洁实现方式如下:

    1. import { _http } from '@/http.service';
    2. import {
    3. debounceTime,
    4. mergeMap,
    5. map,
    6. catchError,
    7. retryWhen,
    8. delayWhen,
    9. } from 'rxjs/operators';
    10. const limit = 100
    11. const len = Math.ceil(this.lineIds.length / limit)
    12. const list = []
    13. let count = 0
    14. for (let i = 0; i < len; i++) {
    15. list.push(idList.slice(i * limit, (i + 1) * limit))
    16. }
    17. from(list) // 分批
    18. .pipe(
    19. mergeMap((data: any) => { // 拿到当前批次数据合并到当前流
    20. let countLimit = 0
    21. return _http // 发送请求,详见 随后贴出http.service
    22. .post('/api/issued-task', {
    23. data
    24. })
    25. .pipe(
    26. map(({ response }: any) => {
    27. const { code, msg } = response
    28. if (code === 1 && countLimit < 3) {
    29. countLimit++
    30. this.alert( // 封装的提示信息方法,
    31. `发送失败,${2 ** countLimit} 秒后自动重发,请稍等...`
    32. )
    33. throw new Error(msg)
    34. }
    35. return response
    36. }),
    37. retryWhen((errors: any) => // 重试
    38. errors.pipe(delayWhen(() => timer(2 ** countLimit * 1000)))
    39. //重试时间设置
    40. ),
    41. catchError(({ message: msg }: any) => of({ code: 1, msg }))
    42. // 捕获 整个请求过程中的错误信息
    43. )
    44. }),
    45. catchError(({ message: msg }: any) => of({ code: 1, msg }))
    46. // 捕获整个分批过程中的错误信息
    47. )
    48. .subscribe(({ code, msg }: any) => { // 每一批次的请求回调处理
    49. count++
    50. if (code !== 0) {
    51. this.alert(msg)
    52. return
    53. }
    54. if (count === len) { // 所有批次下发完成,成功和失败,具体逻辑自己控制
    55. this.alert('任务下发成功!')
    56. }
    57. })

    http.service 内容如下:

    1. /*
    2. * Created by yyccmmkk on 2019/9/11 15:12
    3. * 36995800@163.com
    4. */
    5. import { ajax } from 'rxjs/ajax'
    6. import { of, race } from 'rxjs'
    7. import { delay } from 'rxjs/operators'
    8. const baseURL: string = process.env.VUE_APP_BASE_URL // 上传接口 baseUrl
    9. const regExp = /\/index\/(.+)\/?/
    10. const urlRegExp = /\/(tms|ofc)(?=\/)/
    11. const basePath = process.env.VUE_APP_BASE_PATH // 路由
    12. const mapBasePath = process.env.VUE_APP_MAP_BASE_PATH
    13. const limitBasePath = process.env.VUE_APP_LIMIT_BASE_PATH
    14. const match = window.location.href.match(regExp)
    15. const axios = require('axios')
    16. const token = sessionStorage.getItem('token') || (match && match[1]) || null
    17. const instance = axios.create({
    18. baseURL,
    19. timeout: 50000,
    20. headers: {
    21. 'Content-Type': 'application/json;charset=UTF-8',
    22. token
    23. }
    24. })
    25. // eslint-disable-next-line
    26. class _http {
    27. static cache: any = { source: { url: '', data: null, baseURL } }
    28. static interceptors: any = {
    29. // eslint-disable-next-line
    30. beforeRequest(source: { url: string; data: any; baseURL: string }) {}
    31. }
    32. static post(url: string, data: any) {
    33. const cache = _http.cache
    34. cache.source = { url, data, baseURL }
    35. _http.interceptors.beforeRequest(_http.cache.source)
    36. return race(
    37. of({ code: 408, msg: '请求超时!' }).pipe(delay(50000)),
    38. ajax({
    39. url: `${baseURL}${url}`,
    40. method: 'POST',
    41. async: true,
    42. body: data,
    43. headers: {
    44. 'Content-Type': 'application/json;charset=UTF-8',
    45. token
    46. }
    47. })
    48. )
    49. }
    50. }
    51. // 本地联调时,不需要tms ofc
    52. if (process.env.VAUE_APP_API_ENV === 'local') {
    53. instance.interceptors.request.use(
    54. (config: any) => {
    55. const { url, baseURL } = config
    56. // Do something before request is sent
    57. config.url = url.replace(urlRegExp, '')
    58. const match = url.match(urlRegExp)
    59. if (match) {
    60. config.baseURL =
    61. match[1] === 'ofc' ? `${baseURL}:30210` : `${baseURL}:30250`
    62. }
    63. return config
    64. },
    65. (error: any) => {
    66. // Do something with request error
    67. return Promise.reject(error)
    68. }
    69. )
    70. _http.interceptors.beforeRequest = (source: {
    71. url: string
    72. data: any
    73. baseURL: string
    74. }) => {
    75. const { url } = source
    76. const match = url.match(urlRegExp)
    77. if (match) {
    78. const temp = match[1] === 'tms' ? ':3030' : ':8080'
    79. source.url = url.replace(urlRegExp, temp)
    80. }
    81. }
    82. }
    83. export default instance
    84. export { baseURL, basePath, mapBasePath, limitBasePath }
    85. export { instance as http, _http }