• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1function RetryOperation(timeouts, options) {
2  // Compatibility for the old (timeouts, retryForever) signature
3  if (typeof options === 'boolean') {
4    options = { forever: options };
5  }
6
7  this._originalTimeouts = JSON.parse(JSON.stringify(timeouts));
8  this._timeouts = timeouts;
9  this._options = options || {};
10  this._maxRetryTime = options && options.maxRetryTime || Infinity;
11  this._fn = null;
12  this._errors = [];
13  this._attempts = 1;
14  this._operationTimeout = null;
15  this._operationTimeoutCb = null;
16  this._timeout = null;
17  this._operationStart = null;
18
19  if (this._options.forever) {
20    this._cachedTimeouts = this._timeouts.slice(0);
21  }
22}
23module.exports = RetryOperation;
24
25RetryOperation.prototype.reset = function() {
26  this._attempts = 1;
27  this._timeouts = this._originalTimeouts;
28}
29
30RetryOperation.prototype.stop = function() {
31  if (this._timeout) {
32    clearTimeout(this._timeout);
33  }
34
35  this._timeouts       = [];
36  this._cachedTimeouts = null;
37};
38
39RetryOperation.prototype.retry = function(err) {
40  if (this._timeout) {
41    clearTimeout(this._timeout);
42  }
43
44  if (!err) {
45    return false;
46  }
47  var currentTime = new Date().getTime();
48  if (err && currentTime - this._operationStart >= this._maxRetryTime) {
49    this._errors.unshift(new Error('RetryOperation timeout occurred'));
50    return false;
51  }
52
53  this._errors.push(err);
54
55  var timeout = this._timeouts.shift();
56  if (timeout === undefined) {
57    if (this._cachedTimeouts) {
58      // retry forever, only keep last error
59      this._errors.splice(this._errors.length - 1, this._errors.length);
60      this._timeouts = this._cachedTimeouts.slice(0);
61      timeout = this._timeouts.shift();
62    } else {
63      return false;
64    }
65  }
66
67  var self = this;
68  var timer = setTimeout(function() {
69    self._attempts++;
70
71    if (self._operationTimeoutCb) {
72      self._timeout = setTimeout(function() {
73        self._operationTimeoutCb(self._attempts);
74      }, self._operationTimeout);
75
76      if (self._options.unref) {
77          self._timeout.unref();
78      }
79    }
80
81    self._fn(self._attempts);
82  }, timeout);
83
84  if (this._options.unref) {
85      timer.unref();
86  }
87
88  return true;
89};
90
91RetryOperation.prototype.attempt = function(fn, timeoutOps) {
92  this._fn = fn;
93
94  if (timeoutOps) {
95    if (timeoutOps.timeout) {
96      this._operationTimeout = timeoutOps.timeout;
97    }
98    if (timeoutOps.cb) {
99      this._operationTimeoutCb = timeoutOps.cb;
100    }
101  }
102
103  var self = this;
104  if (this._operationTimeoutCb) {
105    this._timeout = setTimeout(function() {
106      self._operationTimeoutCb();
107    }, self._operationTimeout);
108  }
109
110  this._operationStart = new Date().getTime();
111
112  this._fn(this._attempts);
113};
114
115RetryOperation.prototype.try = function(fn) {
116  console.log('Using RetryOperation.try() is deprecated');
117  this.attempt(fn);
118};
119
120RetryOperation.prototype.start = function(fn) {
121  console.log('Using RetryOperation.start() is deprecated');
122  this.attempt(fn);
123};
124
125RetryOperation.prototype.start = RetryOperation.prototype.try;
126
127RetryOperation.prototype.errors = function() {
128  return this._errors;
129};
130
131RetryOperation.prototype.attempts = function() {
132  return this._attempts;
133};
134
135RetryOperation.prototype.mainError = function() {
136  if (this._errors.length === 0) {
137    return null;
138  }
139
140  var counts = {};
141  var mainError = null;
142  var mainErrorCount = 0;
143
144  for (var i = 0; i < this._errors.length; i++) {
145    var error = this._errors[i];
146    var message = error.message;
147    var count = (counts[message] || 0) + 1;
148
149    counts[message] = count;
150
151    if (count >= mainErrorCount) {
152      mainError = error;
153      mainErrorCount = count;
154    }
155  }
156
157  return mainError;
158};
159