Running JavaScript… With Sneakers!

Code-review time. I haven’t written significant JavaScript in forevs, but I hit upon a use case well suited to it, had a blast coding it up, and am confident that I’ll be completely mystified by it three months from now. That is, unless we refactor it for maintainability (this is where you come in).

I’ll explain the use case. The Couch-to-5K running plan is an excellent exercise regimen for learning to run. I followed it to successfully transform myself from sedentary software developer to 5K finisher. The runners among you know that five kilometers is a beginner-level distance; the sedentary software developers may find it as daunting as I once did. You train in intervals, alternating walking and running, gradually shifting the ratios to more running in successive workouts. If you can walk a moderate distance, following the program is totally doable (or modify it for easy walking/fast walking, if you’re not up for jogging). Definitely check it out.

The thing is, timing the intervals—especially at the beginning, where you’re alternating a minute of running with 90 seconds of walking—needs a complex timing device (a fancy runners watch), and if you’re using a treadmill, it is really annoying to your fellow gym users, as your watch is beeping every minute or two. Or, well, maybe gym people don’t care, but it’s really annoying to me, since the dang thing is on my own wrist. For running indoors, I want a C25K timer that is tactile or visual. I’ve been thinking about an Arduino-powered timer that reads from a dip switch to determine which workout you’re running and signals the changes with a vibration motor (like in a cell phone). Thinking, and not so much doing. Finally one Sunday I forced myself to focus on the problem: what do you have, with a visual display, that is really small and runs code?

My Nokia N810 internet tablet has a web browser that runs JavaScript, can open html files stored locally, and can do this while also playing a podcast. Win!

Here’s what you can’t do with JavaScript, though: Thread.Sleep(walkInterval). Instead, you use setTimeout() to say “execute this function after this interval,” asynchronously. That’s the part that currently works but I daren’t touch it. In other words, that’s the part that needs your code review advice. Remember that the intended scenario is to rest the tablet on the treadmill’s magazine lip, so the app flashes the background of the page to catch my eye when I’m not looking directly at it. It’s high-contrast because it isn’t meant to be watched, just kept at hand, in my periphery.

If you choose to use the app (in addition to reviewing it), please only use it on a treadmill. Stay safe; don’t try to wrangle something you need to look at while you’re on the trail.

The Running Timer code is on github. A representative sample is below. How would you make it better?

var exercise = function(setup) {
	var workout = getWorkout(setup.week, setup.day);
	doExercise(workout, 0);
}

var cooldown = function() {
	walk('Cool Down');
	window.setTimeout(function() { transition(function() { walk('Done!'); })}, 
		minToMilli(RT.cooldownDuration));
}

var doExercise = function(workout, i) {
	workout[i].mode();
	if (i === workout.length - 1) {
		window.setTimeout(function() { transition(cooldown) }, 
			minToMilli(workout[i].minutes));
	} else {
		window.setTimeout(function() { transition(function() { doExercise(workout, i + 1); })}, 
			minToMilli(workout[i].minutes));
	}
}

var getWorkout = function(week, day) {
	return eval('C25K.W' + week + 'D' + day);
}

var transition = function(callback) {
	var indicatorDiv = $('.runCountdown');
	var body = $(document.body);
	transitionBlink(6, indicatorDiv, body, callback);
}

Related Articles:

About Sharon Cichelli

I am a Headspring Senior Consultant, developing custom enterprise software for our clients and leading training classes on the latest Microsoft technologies. I blog about .NET development, best practices in agile software development, and my nerdy hobbies.
This entry was posted in arduino, JavaScript, refactoring. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://profiles.google.com/mdpopescu Marcel Popescu

    Interesting. I don’t see anything obviously bad in the code – I wouldn’t have gone the “eval” route, preferring just a regular pseudo-array [1] but other than that, the code is pretty clear IMO. The doExercise function looks like it wants to be a member function of the workout object but I think that’s art for art’s sake; this was not supposed to be enterprise-level code.

    [1] Given that both the week and the day are one digit you could use a c25k[wd] indexing scheme, where w is the week and d is the day – so c25k[42] is the schedule for week 4, day 2

    • Anonymous

      Thanks for the thoughtful feedback. Nice point about the pseudo-array for finding the workout; I’ll try that out. Regarding moving “doExercise” to be a member of workout, I think I follow you. I am not yet at all used to object-oriented JavaScript, so I’m still developing my intuitions in that regard. I’ll look into that. Thanks very much for the suggestions.

  • http://mutedsolutions.com Derick Bailey

    The big thing I’ll point out is the excessive pollution of the JavaScript global object / context. In such a small, specialized app it isn’t a big deal. When you get into larger apps, apps that include more libraries, and generally have more code, this becomes a problem quickly.

    There’s a performance penalty every time you have to look for a global variable / function / object, because JavaScript has to first search the local scope, then the bubble up through scopes / closures, then finally get to the global context for one last search.

    Worse, though, is that it’s really easy to accidentally overwrite someone else’s code by using a variable or function name that another chunk of code needed. Think about namespaces in other languages, and the same basic principles of organization apply here.

    To combat the problems, look at the JavaScript module pattern. I introduce this a little in my most recent post on composite JavaScript apps, and there’s a ton of other info out there that goes in to much more detail.

    The gist of it, though, is that you can wrap up your code in a function (functions inside of functions…) and then provide a “public” API that is returned from the function, to kick off your app.

    MyApp = (function(){
      var somePrivateVar = “foo”;
      var somePrivateFunction = function(){
      }

      // the public stuff
      var myApp = {};
      myApp.doStuff = function(){
      }
      // it’s “public” because it’s being returned, here
      return myApp;
    })();

    $(function(){
      MyApp.doStuff();
    });

    • Anonymous

      Nice! Yeah, I know the app is small, but part of the exercise is writing it really cleanly, so that I can practice the big concepts–so that’s exactly the kind of help I’m looking for. Thanks for the extra context on “why,” in addition to “what.” This weekend I’m gonna finally sit down and digest your WatchMeCode screencast on JavaScript refactoring, too.

  • Anonymous

    Taking a cue from Garann Means’ talk at HTML5.tx, one way to simplify the JavaScript is to replace it with CSS3. :) So I made a JsFiddle that uses CSS to do the background flashing. http://jsfiddle.net/scichelli/JWpeV/

    However, the browser on my N810 doesn’t support CSS3, and due to the existence of the N900 (and Android, sigh), it is unlikely ever to do so.