retry.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. "use strict";
  2. var __importDefault = (this && this.__importDefault) || function (mod) {
  3. return (mod && mod.__esModule) ? mod : { "default": mod };
  4. };
  5. Object.defineProperty(exports, "__esModule", { value: true });
  6. const retry_1 = __importDefault(require("retry"));
  7. // import { RetryOperation } from 'retry/lib/retry_operation'
  8. const RetryOperation = require('retry/lib/retry_operation');
  9. /* istanbul ignore next */
  10. function defaultShouldRetry(e, result) {
  11. return { retryAble: false, message: '' };
  12. }
  13. /**
  14. * withRetry 重试封装函数
  15. * @param fn
  16. * @param retryOptions
  17. */
  18. /* istanbul ignore next */
  19. function withRetry(fn, retryOptions) {
  20. // 默认不重试,0 表达未开启的含义,所以直接返回 promise
  21. if (!retryOptions || retryOptions.retries === 0) {
  22. return fn();
  23. }
  24. // 默认重试策略采取指数退避策略,超时时间计算公式及参数可查文档
  25. // https://github.com/tim-kos/node-retry/
  26. // 自定重试时间:
  27. // timeouts: [1000, 2000, 4000, 8000]
  28. const timeouts = retryOptions.timeouts
  29. ? [...retryOptions.timeouts]
  30. : retry_1.default.timeouts(retryOptions);
  31. const operation = new RetryOperation(timeouts, {
  32. forever: retryOptions.forever,
  33. unref: retryOptions.unref,
  34. maxRetryTime: retryOptions.maxRetryTime // 重试总的时间,单位毫秒,默认:Infinity
  35. });
  36. const shouldRetry = retryOptions.shouldRetry || defaultShouldRetry;
  37. return new Promise((resolve, reject) => {
  38. const isReadyToRetry = (e, resp, operation) => {
  39. // 外层有效识别需要或者能够进行重试
  40. // shouldRetry 中可调用 operation.stop 停掉重试,operation.retry 返回 false
  41. const { retryAble, message } = shouldRetry(e, resp, operation);
  42. const info = {};
  43. info.nth = operation.attempts();
  44. info.at = new Date();
  45. info.message = message;
  46. // 双重条件判断是否重试,外层判断满足条件与否,还需判断是否满足再次重试条件
  47. const readyToRetry = retryAble && operation.retry(Object.assign({}, info));
  48. if (!readyToRetry) {
  49. // 如果不准备进行重试,并且尝试不止一次
  50. // 最后一个错误记录重试信息
  51. const ref = e || resp;
  52. if (ref && operation.attempts() > 1) {
  53. ref.attempt = {};
  54. ref.attempt.timeouts = operation._originalTimeouts;
  55. ref.attempt.attempts = operation.attempts();
  56. ref.attempt.errors = operation.errors();
  57. // 如果最后一次因为 !retryAble 而没有进行重试
  58. // ref.attempt.errors 中将缺少最后的这个错误
  59. // ref.attempt.errors 中包含最后一次错误信息
  60. if (!retryAble) {
  61. ref.attempt.errors.push(info);
  62. }
  63. }
  64. }
  65. return readyToRetry;
  66. };
  67. operation.attempt(async () => {
  68. try {
  69. const result = await fn(operation.attempts());
  70. if (!isReadyToRetry(null, result, operation)) {
  71. resolve(result);
  72. }
  73. }
  74. catch (e) {
  75. try {
  76. if (!isReadyToRetry(e, null, operation)) {
  77. reject(e);
  78. }
  79. }
  80. catch (e) {
  81. reject(e);
  82. }
  83. }
  84. }, retryOptions.timeoutOps);
  85. });
  86. }
  87. exports.withRetry = withRetry;