Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Yet Another Hard Lesson About Time

DZone's Guide to

Yet Another Hard Lesson About Time

Time is quite an elusive thing to program for. Check out this first hand account about creating a timer in React and MobX and learn from Swizec's experience.

· Web Dev Zone
Free Resource

Try RAD Studio for FREE!  It’s the fastest way to develop cross-platform Native Apps with flexible Cloud services and broad IoT connectivity. Start Your Trial Today!

- Yo Swiz, some users complained their exam ends immediately.

- Hm… that's weird

So began my two-day hunt for a bug.

To become a Yup tutor, you have to pass a subject exam: 25 questions about math, chemistry, or physics in 45 minutes. It's hard as hell; I tried.






There's a timer in the corner ticking down. 45 minutes, and then you're done. A dialog shows up telling you time’s up. Easy peasy to build, right? Get a start time, add 45 minutes, count down. When the timer hits zero, stop. 90% of the time, it works every time.

And then there’re those 7 people. For them, the timer hits zero 1 second after it starts. 45 minutes. Tick. Zero.

o.O

I looked through the code. No race conditions. You click Start, the server creates an exam, makes a started_at timestamp, adds 45 minutes, and tells the frontend when your exam should end. On every Clock tick, the counter updates, and a MobX observer checks if you're out of time.

Very straightforward.

We're using React and MobX, so the front-end behaves like a state machine. There's no way we're rendering stale views, no way for poorly garbage collected views to cause problems, and the more I played with the front end state machine, the sturdier it looked.

Here's an excerpt:

class SubjectExam {
    constructor(user, subject) {
        when(() => this.isOutOfTime,
             () => {
                 if (!this.finished) {
                     this.finish();
                 }
             }
        );
    }

    @computed get isOutOfTime() {
        return !_.isNull(this.timeLeft) && this.timeLeft <= 0;
    }

    @computed get timeLeft() {
        if (this.should_end_by) {
            return moment.duration(moment.unix(this.should_end_by).diff(Clock.time));
        }else{
            return null;
        }
    }
}


MobX's when creates an autorun that checks the condition function, () => this.isOutOfTime, every time its value changes. When it evaluates to true, it runs the given function. In this case, that’s an action that finishes the subject exam.

The reason this works is hidden in @computed timeLeft(). It depends on Clock.time, which is a global MobX store that updates every second. It’s based on my Modeling time in React & MobX approach.

Diffing the server-provided should_end_by timestamp and Clock.time tells us how much time is left. It works great and produces a countdown timer when you add some rendering.

The nice feature of this approach is that the timer is stable. You can refresh and navigate the page as much as you want. The server calculates the time, so it doesn't care.

But… those 7 people… why was the time up after 1 second? Can you guess?

I found no pattern in who the applicants were, when they took the exam, what time zone they were in, or which browser they used. It's not a bug in MomentJS. None of the cases happened around a DST, day, month, or year boundary.

It should work.

The only remaining explanation is that their system time was waaaaaayyy in the future. More than 45 minutes. I don't know why their computers thought they were in the future, but it's the only explanation that works.

How do you fix it?

You don't rely on user time. Instead of calculating a should_end_by timestamp on the server, calculate the delta. Look only at the delta when doing a countdown.

Like this:

The exam starts, the server looks at current time and returns 45min. User reloads, server looks at current time, calculates delta from start time, returns new time remaining.

On the frontend, you create a local shouldEndAt timestamp when you load the exam – time + delta_from_server – then count down from there.

Lesson learned. Time is so hard that you can't even rely on people's clocks being accurate to within 1 hour. Add it to the Falsehoods Programmers Believe About Time pile.

Get Your Apps to Customers 5X Faster with RAD Studio. Brought to you in partnership with Embarcadero.

Topics:
exam ,observer ,new ,timer ,state machine

Published at DZone with permission of Swizec Teller, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}