Home Reference Source Repository

src/lib/backoff-wrapper.js

  1. import {EventEmitter} from 'events'
  2. import ExponentialBackoffStrategy from 'backoff/lib/strategy/exponential'
  3.  
  4. /*
  5. This is heavily based on:
  6. https://github.com/MathieuTurcotte/node-backoff/blob/master/lib/backoff.js
  7.  
  8. ...which holds the following 2-line copyright notice:
  9. Copyright (c) 2012 Mathieu Turcotte
  10. Licensed under the MIT license.
  11.  
  12. This is a very bare-bones version of the file above
  13. */
  14.  
  15. /**
  16. * A wrapper over the ExponentialBackoffStrategy from the backoff module.
  17. */
  18. export default class ExponentialBackoff extends EventEmitter {
  19. /**
  20. * Constructor.
  21. */
  22. constructor() {
  23. super()
  24.  
  25. /**
  26. * ExponentialBackoffStrategy instance.
  27. * @type {ExponentialBackoffStrategy}
  28. */
  29. this._strategy = new ExponentialBackoffStrategy({
  30. randomisationFactor: 0.3,
  31. initialDelay: 1000,
  32. maxDelay: 60000
  33. })
  34.  
  35. /**
  36. * Number of previous failed attempts.
  37. * @type {Number}
  38. */
  39. this._backoffNumber = 0
  40.  
  41. /**
  42. * Backoff timeout ID.
  43. * @type {TimeoutID}
  44. */
  45. this._timeoutID = undefined
  46. }
  47.  
  48. /**
  49. * Signals that we tried executing the protected routine and failed, or that we want to start the process.
  50. * @emits backoff(_backoffNumber, delay) delay is how much time will pass before we try executing the routine.
  51. */
  52. backoff() {
  53. // Backoff already in progress
  54. if (this._timeoutID) {
  55. return
  56. }
  57.  
  58. const delay = this._strategy.next()
  59. this._timeoutID = setTimeout(() => {
  60. this._timeoutID = undefined
  61. this.emit('retry', this._backoffNumber)
  62. this._backoffNumber++
  63. }, delay)
  64. this.emit('backoff', this._backoffNumber, delay)
  65. }
  66.  
  67. /**
  68. * Resets failed attempt number and cancels a possible pending action (routine execution after backoff).
  69. */
  70. reset() {
  71. // Reset number of backoffs
  72. this._backoffNumber = 0
  73.  
  74. // Reset strategy
  75. this._strategy.reset()
  76.  
  77. // Clear a possibly running timeout
  78. if (this._timeoutID !== undefined) {
  79. clearTimeout(this._timeoutID)
  80. this._timeoutID = undefined
  81. }
  82. }
  83.  
  84. /**
  85. * Used to signal the routine was successful.
  86. * This resets the wrapper.
  87. * @emits success
  88. * @see {@link reset}
  89. */
  90. success() {
  91. // Reset state
  92. this.reset()
  93.  
  94. // Alert everyone we did it (for now at least)
  95. this.emit('success')
  96. }
  97. }