A better JS INTERVAL that evaluates ASYNC function SEQUENTIALLY, it’s pausable/resumable and can be forced to run

Fredo Corleone
3 min readOct 10, 2021

When you need to fetch data periodically from a server you usually use setInterval(), pass it a job and a delay and this will keep calling your job at a constant rate given by the delay.

This is acceptable for most applications since the delay from your server to the client will be smaller than the rate give by your delay.
BUT… It does NOT a good job when you have slow connection or you are trying to fetch data sequentially.

You have to know that setInterval() doesn’t wait for your async job to be completed, it runs blindly.
Therefore in case or slow connectivity you can end up accumulating old responses that might (depending on what you do in your code) override newer and fresher data.

I decided to give this problem a shot and I came up with a cool neat constructor that it’s like the setInterval() but evaluates your async jobs sequentially (won’t call again unless the previous has finished) and it’s also startable/stoppable and forced to execute!

As usual I wrote down a bullet list how want I wanted:

A SmartInterval that creates an interval that has the following features:
- Can be paused/stopped
- Can be forced to execute
- Evaluates sequentially (no other call is made before the current call is evaluated)

Code example

Here you can see how simple the API of this SmartInterval is:

let SmartInterval = require("smartinterval");let dataFetcher = new SmartInterval(
async () => {
await getSomeData();
await getSomeOtherData();
}
, 3000
);
dataFetcher.start();dataFetcher.forceExecution();dataFetcher.stop();

How it’s made: the constructor

The constructor accepts 2 arguments exactly as setInterval() does, the first is the async job you want to run on a give time basis, the second is the minimum amount of milliseconds between each job call.

function SmartInterval(asyncFn, delayMs) {
this.asyncFn = asyncFn;
this.delayMs = delayMs;
this.running = false;
}

The cycle

The cycle is the main method behind the magic of all this.
It awaits the job call.
Await for a certain delay.
Rinse and repeat.

SmartInterval.prototype.cycle = async function (forced) {
await this.asyncFn();
await this.delay(this.delayMs);
if (!forced && this.running) this.cycle();
};

The delay

The delay is a promise that resolves after a give time.
Being it a promise is awaitable by default, therefore you can pause at it for the give amount of milliseconds.
It’s the ingenous lil trick behind the minimum delay between calls described above.

// This function is just an arbitrary delay to be used with async/await pattern
SmartInterval.prototype.delay = function (ms) {
return new Promise(res =>
setTimeout(() => res(1), ms)
);
};

How to start

If already running, then it does nothing. Else if calls the cycle() method and changes the internal state of the interval.

SmartInterval.prototype.start = function () {
if (this.running) return;
this.running = true;
this.cycle();
};

How to stop

It changes the internal state of the interval so that the cycle() method isn’t called anymore (therefore stopping the vicious cycle).

SmartInterval.prototype.stop = function () {
if (this.running) this.running = false;
};

How to force a cycle

To force the execution it’s as simple as calling the cycle() method manually.
It’s passing an argument to make an exception so that the call doesn’t spawn another long-lasting loop inside the cycle.

SmartInterval.prototype.forceExecution = function () {
if (this.running) this.cycle(true);
};

Conclusion

You can find this on GitHub:

https://github.com/4skinSkywalker/SmartInterval

And as an NPM package:

https://www.npmjs.com/package/smartinterval

--

--