Last active
April 28, 2024 23:49
-
-
Save ivanbanov/14d2288dd82b04b4e9cba745181c92f3 to your computer and use it in GitHub Desktop.
Implements a polling with max retries and exponential retries
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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