I have a main stopwatch with 4 mini-stopwatches for each step. After a finished time, here is an example of how the timers should look:
MAIN: 00 : 14 : 57
-------------------
MINI1: 00 : 04 . 17
MINI2: 00 : 06 . 40
MINI3: 00 : 02 . 54
MINI4: 00 : 01 . 46
The mini-timers should add up to the main timer, as they do in this case. With my current timer, it always seems to be .02
milliseconds off, so they would add up to 00 : 14 . 55
in this case instead of 00 : 14 . 57
.
Here is a JSFiddle of my current timers. I think the issue is most likely in the stopwatch.js
file, but I'm not sure why that would be the case since I'm using Date.now()
to calculate how much time has passed. Here is the stopwatch.js
file which is the code for an individual stopwatch:
class Stopwatch {
constructor (opts) {
this.isOn = false;
this.time = 0;
this.elem = opts.elem;
}
start () {
this.offset = Date.now();
this.interval = setInterval(() => this._update(), 10);
this.isOn = true;
}
stop () {
clearInterval(this.interval);
this.offset = null;
this.interval = null;
this.isOn = false;
}
reset () {
this.time = 0;
this._render();
}
_update () {
this.time += this._getTimePassed();
this._render();
}
_getTimePassed () {
const now = Date.now();
const timePassed = now - this.offset;
this.offset = now;
return timePassed;
}
_timeFormatter (milliseconds) {
const padZero = (time) => `0${time}`.slice(-2);
const minutes = padZero(milliseconds / 60000 | 0);
const seconds = padZero((milliseconds / 1000 | 0) % 60);
const centiseconds = padZero((milliseconds / 10 | 0) % 100);
return `${minutes} : ${seconds} . ${centiseconds}`;
}
_render () {
this.elem.textContent = this._timeFormatter(this.time);
}
}
I have everything altogether inside the JSFiddle I mentioned above and also in this gist if that's easier to read. Any guidance would be appreciated.
You're talking about a 20ms delay, which can be caused by a number of things.
Date.now()
setInterval
drifts! And here's a bin to prove it. You can't really expect for a language that runs on a single thread to schedule tasks every 10ms with perfect accuracy, and your _update
method demands just that.To solve this correctly, I'd recommend redesigning your solution so that you wrap all your stopwatches in a StopwatchManager
, which renders all your stopwatches at once and computes the total time for your main watch by adding the times of the miniwatches. You might also want to look into using requestAnimationFrame
for rendering instead of setInterval
.
You're stopping one timer, and in the next line, starting the next. Your problem is partially that time is passing between those two method calls.
Also, your 'stop' method doesn't even use the current time, it just retroactively stops it from the last time it was updated, it doesn't do a final _update
.
If you really want it to add up precisely, take Date.now()
in your updateMiniTimers
method, and pass that to both calls to make sure they stop/start at the same point in time, and do a final render after a stop
call.
In general, within the method:
method() {
var a = Date.now();
var b = Date.now();
}
a and b are absolutely not guaranteed to be the same, no method call is instantaneous.
I concur with Faraz about possible causes. I would also check for rounding errors in the divisions. Anyway, if you want to make it a bit more robust and scalable, you could think about your time points as elements in a List that keeps expanding. Whenever a Stopwatch starts, you record the index of the last element in the List as Starting Point, whenever one ends you record the the index of the last Element as the End Point. That would allow you to have accurate hierarchies of timers.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With