Exercise: Workout Interval Timer
Build the engine of a workout interval timer, a plan of work and rest, a clock display and a countdown that runs on setInterval, and let tests with a pretend clock check it in a split second.
Journeyman · JavaScript · Updated
Interval training is how a lot of people work out: 30 seconds of jumping jacks, 10 seconds of rest, again and again for a few rounds. Doing it with a phone's stopwatch means a lot of tapping with sweaty hands, so a timer that runs the whole workout by itself is worth having. The page is ready: it has the settings, a Start and a Stop button, and a big clock that turns red for work and green for rest. What it needs is the engine underneath, three functions for you to write, and tests check every one as you go.
Get the exercise code
In the Exercise box beside the lesson, choose Get the starter on GitHub to open its GitHub repository. Click Code, then Download ZIP, and unzip it where you keep your practice folders. Or clone it with Git.
The folder holds:
timer.js: the three functions, for you to finish.timer.test.js: the tests that check your functions. You don't need to change it.workout.js: runs a short workout in the terminal with your functions. Ready to use.index.html,style.cssandapp.js: the timer's page, and the code that connects it to your functions. Ready to use.package.json: tells Node.js how to run the code and its tests. Ready to use.
Open the folder in VS Code, and open a terminal there with Terminal > New Terminal.
What to do
Every function is already declared, with its parameters, and returns a placeholder for now. The comment above each one says what it should do, with examples:
// 2. formatClock(seconds)
// Return a number of seconds the way a clock shows it: minutes, a colon, then two digits of seconds.
// formatClock(30) -> '0:30'
// formatClock(5) -> '0:05'
// formatClock(65) -> '1:05'
// formatClock(600) -> '10:00'
export function formatClock(seconds) {
return '';
}
- Finish
intervalPlan(rounds, workSeconds, restSeconds). It returns the workout as an array of steps, such as{ name: 'Work', round: 1, seconds: 30 }. Every round is a Work step and then a Rest step, except the last round, which has no rest. It's a pure function: it works only from its arguments and changes nothing outside itself, so the same arguments always give the same plan. - Finish
formatClock(seconds). It turns a number of seconds into what the clock shows, from0:05to10:00. - Finish
startTimer(plan, onTick, onDone), withsetInterval. It callsonTickright away for the first step, then once a second as it counts down, moving on to the next step when one runs out. At the end it stops its interval and callsonDoneonce. It returns a function that stops the timer early, which the page's Stop button calls.
Start with the first two: they have no timers in them at all. startTimer is the one that waits.
Check your work
Run the tests from the terminal:
node --test
At first all 11 tests fail. That's expected: each function you finish turns its tests green. When a test fails, its message says what it expected, for example 5 seconds shows as 0:05: the seconds always have two digits.
A workout lasts minutes, but the tests take a split second. They replace setInterval with a pretend clock, called a mock timer, and move it forward a second at a time with t.mock.timers.tick(1000), so no test ever really waits. One thing follows from that: the pretend clock only knows about setInterval, so startTimer has to use it (a chain of setTimeout calls won't pass).
To see your functions run a short workout in real time, two rounds of 3 seconds of work and 2 of rest:
node workout.js
Then open index.html with Live Preview in your browser, set a workout and press Start. Check the page against this list:
- The clock shows the first step's full time as soon as you press Start, then counts down once a second.
- The box turns red for work and green for rest, and the line above the clock says which round you're on.
- After the last round there's no rest: the page says Workout complete! Great job.
- Stop stops the clock at once, and Start starts a new workout from the top.
Hints
- Work out a small plan by hand first. For 3 rounds of 20 seconds' work and 15 seconds' rest, write the steps down in order. How many are there, and why isn't it 6?
- A
forloop builds the plan. Count the rounds from 1, push a Work step each time, and use anifto decide whether this round gets a Rest step after it. - Two numbers make the clock: the whole minutes, and the seconds left over.
Math.floorrounds down (you used it in Parameters and Return Values), and%gives a remainder.padStart, from String Methods, gives5its leading zero. - Keep the timer's ID.
startTimerneeds it twice: to stop at the end, and inside the function it returns. Timers: setTimeout and setInterval shows an interval that stops itself. - Two variables keep your place: which step you're on, and how many seconds that step has left. Each tick changes one or both.
- Call the callbacks, don't print.
startTimernever usesconsole.log: it hands each tick toonTick, andworkout.jsand the page decide how to show it.
When you're done
All 11 tests are green, and node workout.js prints one line a second, then the last line:
Workout: 3 steps
Round 1 of 2 | Work | 0:03
Round 1 of 2 | Work | 0:02
Round 1 of 2 | Work | 0:01
Round 1 of 2 | Rest | 0:02
Round 1 of 2 | Rest | 0:01
Round 2 of 2 | Work | 0:03
Round 2 of 2 | Work | 0:02
Round 2 of 2 | Work | 0:01
Workout complete! Great job.
Now look back at startTimer. Could the same function run a different kind of plan, say the steps of a recipe, or the turns of a board game, without any change? If it only knows about steps with seconds, and leaves the showing to onTick, the answer is yes.
So what
You've built the part of a timer app that every countdown shares: a plan worked out by a pure function, a display format, and a loop that runs on time without freezing the page. The same pieces run a trivia game's 15-second countdown, a game's level clock and a "your session ends in 2:00" warning.
Key terms
- setInterval()
- clearInterval()
- Callback Function
- Pure Function