When MomentJS is too slow

Following on from Making a Persistent Accurate Timer Without an RTOS I hit an interesting issue with MomentJS that caused me to drop down to just using millisecond timestamps.

SquadWOD has a countdown timer, at 3, 2, and 1 second left I want to play a chime to indicate that the countdown is ending and you're about to start the workout. Initially I went down this route:

const endTime = this.timer.timerEndTime.clone();
if (moment().isAfter(endTime.subtract(1, 'seconds'))) {
  console.log('less than 1');
} else if (moment().isAfter(endTime.subtract(2, 'seconds'))) {
  console.log('less than 2');
} else if (moment().isAfter(endTime.subtract(3, 'seconds'))) {
  console.log('less than 3');
}

This code is in the RxJS subscription to the timer interrupt that overflows every 10ms. As discussed in the above article, this interrupt and subscription is just for display purposes, the important bit is the timerEndTime Moment that determines exactly when the timer expires.

The above code works logically but when executed every 10ms it falls apart and I was getting 'less than 3' logs anywhere from 8 seconds left to 5 seconds left.

A bit of research on this indicated that the problem may lie with the .isAfter() function, it seems to be a bit slow. So what to do?

Since I don't actually care about the actual time of these events (current time vs timer end) the convenience I get from using the MomentJS library is minimal in this instance, so the approach I settled on was to drop down to the millisecond Unix timestamp using the MomentJS .valueOf() function.

currentCountDownChime = 3; // component property

...

const now = moment().valueOf();
const endTime = this.timer.timerEndTime.valueOf();

if (now > (endTime - (this.currentCountDownChime * 1000))) {
  console.log(`less than ${this.currentCountDownChime}`)
  this.currentCountDownChime--;
}

This code:

  • gets the current Moment in milliseconds
  • gets the timer end Moment in milliseconds
  • looks to see if now is greater than the end time minus the countdown point we are interested in (ie 3, 2, 1)
  • if it is then it logs out and then decrements the countdown point we are interested in by 1, essentially acting as a gate until the next threshold when we want to do something

I could have got the current time from JavaScript's Date class but since the timer end time is a Moment it seems prudent to use the same base when comparing the dates, if they are both off for some reason then it won't affect the relative difference. This is unlikely to be an issue but it's one less potential source of bugs.