debounce-cycle
v0.1.0
Published
Run a function on a set interval, with possibility for early runs
Readme
node-debounce-cycle
Run a function on a set interval, with possibility for early runs
Warnings
There is very little error-checking. Use at your own peril.
If retry is not set, a run that throws stops the cycle: there is no time to wait before retrying,
so nothing is scheduled. Set retry on any cycle whose run function can fail.
Options
min: minimum time between starts in milliseconds, or a function that returns millisecondsmax: maximum time between starts in milliseconds, or a function that returns milliseconds. Used when cycling.jitter: milliseconds to randomly adjust the max (+ or -) to prevent a batch of requests at the same timeretry: minimum time between starts in milliseconds when the run function returns an error, or a function that returns millisecondsimmediate: run as soon as the first run is asked for, rather than waiting outminormaxfirststart: start cycling straight awaylogger: object withdebuganderrormethods
Requesting a run
request() asks for a run and returns a promise that resolves with what the run function returned.
A request made while a run is in flight is answered by the next run, not the one already
underway: whatever that run is fetching, it fetched before being asked, so it cannot be the answer.
Several requests arriving during a run are answered together by one further run, which still waits
out min from the start of the run before it. This is what keeps an update that lands mid-run from
being missed, and it holds whether or not the cycle is also running on max.
A run that fails rejects the requests it was answering and schedules a retry. The retry is itself a run, so it answers anything that came in while the failing run was underway.
What the run function is told
The run function is called with the kind of request that led to the run, so it can tell the cycle's own turn from something asking outright:
const run = async ({runRequestType}) => {
// DebounceCycle.RUNREQUESTTYPE.MIN asked for with request()
// DebounceCycle.RUNREQUESTTYPE.MAX the cycle's own turn
// DebounceCycle.RUNREQUESTTYPE.RETRY following a run that failed
}Stopping
stop() ends the cycle. A run that was asked for with request(), and is still waiting out its
min or retry time, will still happen: stopping the cycle is not a refusal to run.
destroy() stops for good. Nothing is scheduled, no further requests are honored, and a request
made afterwards resolves with no result. A run already underway is not interrupted, since there is
no way to interrupt it, but nothing follows it. Requests still outstanding are resolved with no
result rather than rejected, because a request is commonly made without being awaited and a
rejection nobody is waiting for takes the process down with it.
Examples
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
const run = async ({runRequestType}) => {
const startDate = new Date();
await sleep(500);
return startDate;
}
const controller = new DebounceCycle(run, {
min: 1000, // runs must start at least 1,000ms (1s) apart, except in retry
max: 3000, // when cycling, start every 3,000ms (3s)
jitter: 1000, // when cycling using max, adjust start time by +/- 1,000ms (1s)
retry: 500, // when run returns an error, retry after 500ms
start: false, // don't start cycling
immediate: false // wait for at least min or max (depending on which is requested) on the first run
});
(async () => {
const result1 = controller.request(); // requested with min
const result2 = await controller.request(); // also requested with min
console.log("Result 2", result2);
})();Tests
npm test