Skip to content

Instantly share code, notes, and snippets.

@ivanbanov
Last active April 28, 2024 23:49
Show Gist options
  • Save ivanbanov/14d2288dd82b04b4e9cba745181c92f3 to your computer and use it in GitHub Desktop.
Save ivanbanov/14d2288dd82b04b4e9cba745181c92f3 to your computer and use it in GitHub Desktop.
Implements a polling with max retries and exponential retries
const DEFAULT_BACKOFF = 200; // milliseconds
export class Polling {
constructor(fn, maxRetries = 10, initialDelay = DEFAULT_BACKOFF) {
this._fn = fn;
this._remainingRetries = maxRetries;
this._delay = initialDelay;
}
async perform() {
let result = null;
while (result == null) {
if (this._remainingRetries-- === 0) {
throw new Error('Exceeded maximum retry limit polling');
}
result = await this._fn();
if (result == null) {
this._delay = await this._backoff(this._delay);
}
}
return result;
}
wait(cb, ms) {
setTimeout(cb, ms);
}
async _backoff(delay) {
await new Promise(resolve => this.wait(resolve, delay));
return delay + Math.floor(delay * Math.random());
}
}
export default function polling(...args) {
return new Polling(...args).perform();
}
import { expect } from 'chai';
import { describe, it, context } from 'mocha';
import { default as polling, Polling } from './Polling';
describe('Polling', () => {
it('should call the `fn` untill gets a non null result', async () => {
let count = 0;
await polling(() => (++count === 5 ? count : null), 99, 0);
expect(count).to.eql(5);
});
it('should use exponential backoff', async () => {
let count = 0;
const backoff = [];
const instance = new Polling(() => ++count === 5 ? count : null);
instance.wait = (cb, ms) => {
backoff.push(ms);
cb();
};
await instance.perform();
const grows = backoff.every((time, i) => i === 0 ? true : time > backoff[i - 1]);
expect(grows).to.be.true;
});
context('when do not resolve the polling after all retries', () => {
it('should throw an error', async () => {
let error = false;
try {
await polling(() => null, 1, false);
} catch (e) {
error = true;
}
expect(error).to.be.true;
});
});
context('when resolves the polling', () => {
it('should return the result', async () => {
let count = 0;
const result = await polling(() => ++count === 5 ? count : null, 99, 0);
expect(result).to.eql(5);
});
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment