Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Best way to run something every N seconds, precisely #10

Closed
adrfantini opened this issue Jan 19, 2021 · 11 comments
Closed

Best way to run something every N seconds, precisely #10

adrfantini opened this issue Jan 19, 2021 · 11 comments

Comments

@adrfantini
Copy link

Hi!
I am looking to schedule a task with bree every N seconds. N is chosen by the user.
Let's say N=45s. This means the function should be called e.g. at 13:00:00,13:00:45, 13:01:30, 13:02:15, 13:03:00, ...
Of course this becomes more complex for other Ns, such as N=167.

Ideally I could do it like this:

later = require('later')
const textSched = later.parse.text('every 45 sec');
function logTime() {
    console.log(new Date());
}
const timer = later.setInterval(logTime, textSched);

However, this does not actually run every 45 seconds, but it actually triggers every x:00 and x:45.

Is there any way to schedule something like this?

@niftylettuce
Copy link
Contributor

niftylettuce commented Jan 19, 2021

{ interval: ms('45s') }, you could also use dayjs to build a date to start it at the next whole minute, e.g. { interval: ms('45s'), date: dayjs().add(1, 'minute').startOf('minute').toDate() }

@adrfantini
Copy link
Author

Thank you for your prompt response!

Unfortunately this solution has the issue that it slowly drifts forwards. For N=5s and with a start at 00, it will log at 00, 05, 10, ...
but after a while, since it drifts by a few ms every execution, it will possibly print at 01, 06, 11, ... etc.

Do you know of any solution to this?

@niftylettuce
Copy link
Contributor

The better approach would be to use cron, also see #9

@niftylettuce
Copy link
Contributor

Edit: Not #9, I meant look at local time support in breejs/bree#76 and https://github.com/breejs/issues/74

@niftylettuce
Copy link
Contributor

The drifting is most likely because of code execution time, perhaps there can be optimizations done in our source code further in bree itself.

@adrfantini
Copy link
Author

The better approach would be to use cron, also see #9

Unfortunately from what I understand in CRON the same issue with values such as "every 45 seconds" occurs. Using step values (/45) causes CRON to run at 00:00 and 00:45, but not at 01:30.

The drifting is most likely because of code execution time, perhaps there can be optimizations done in our source code further in bree itself.

Indeed. This is the same behaviour of using chained setTimeouts (first example here: https://javascript.info/settimeout-setinterval#nested-settimeout). However for our usecase this is not acceptable. We need to run every N seconds, regardless of how long the task takes, not simply wait 10 seconds between executions. This is well explained in the link above.
Chained setTimeouts with corrections for this are easy to implement, but it'd be nice to be able to use all the niceties of bree for this.

@niftylettuce
Copy link
Contributor

Could you review our source code to check for places we could optimize to ensure accuracy?

@adrfantini
Copy link
Author

It's not a matter of code efficiency. The issue is that bree uses setInterval or later.setInterval. Both tend to drift.
If you run this code: setInterval(function() {console.log(new Date())}, 2000)
you will get the following output:

2021-01-20T07:21:36.982Z
2021-01-20T07:21:38.985Z
2021-01-20T07:21:40.986Z
2021-01-20T07:21:42.988Z
2021-01-20T07:21:44.990Z
2021-01-20T07:21:46.992Z
2021-01-20T07:21:48.994Z
2021-01-20T07:21:50.995Z
2021-01-20T07:21:52.998Z
2021-01-20T07:21:54.999Z
2021-01-20T07:21:57.002Z
2021-01-20T07:21:59.003Z
2021-01-20T07:22:01.004Z
2021-01-20T07:22:03.006Z
2021-01-20T07:22:05.009Z
2021-01-20T07:22:07.010Z
2021-01-20T07:22:09.011Z

As you can see, it slowly drifts forward by a few ms every execution. After about 2-300 executions on my system, it will have drifted by a whole second (and now it executes on odd seconds instead of even seconds!).

This is well documented by several issues and blogposts. E.g.:

One solution is to use recursive setTimeouts, which automatically compensate after each execution. Or use better timing functions.

Here are a few packages addressing this:

@adrfantini
Copy link
Author

adrfantini commented Jan 20, 2021

So in short this is two issues in one:

  1. not being able to specify intervals like 'every 45 seconds' and have it executed correctly every 45 seconds. This limitation is similar to an identical limitation in CRON.
  2. If using ms('45s') or 45000, there is a drift due to the use of setInterval.

@niftylettuce
Copy link
Contributor

Thank you so much for this @adrfantini. Excellent writeup. By chance are you interested in crafting a pull request to address this?

@adrfantini
Copy link
Author

adrfantini commented Jan 20, 2021

TBH I'm not really sure where to start (and I am a complete JS noob).
I have no idea how to address 1.The way to address 2. would be I guess to not use setInterval and instead use some other more precise method

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants