A JavaScript War Story

What follows is an account from the author’s experience. Some details have been changed for the usual reasons, and a poor memory has fuzzed out the rest.

UPDATE: it turns out the technique described below is known as debouncing. What an awesome name!

My last job was for a company that made case-management software. With this software, you could track all kinds of data about your case: you could categorize it, sub-categorize it, say where and when it happened, note whether it was still open, track who was responsible…all kinds of stuff, great for data fiends.

One of our customers’ favorite features was the reports, and they were pretty spiffin’. But, not being ones to rest on our laurels, we were adding pivot-table reports, so you could count the number of cases, grouped by any criteria you wanted. The input screen let the customer pick their pivot criterion, and which criteria they wanted as columns, for sub-counts. We struggled to name these UI fields — something lucid, something explanatory, something evocative — something to make them understand what kind of report they were in for. After a while, we decided to just show them: when they changed their pivot criterion, we’d run some javascript to render a skeleton report, same as the real report, but minus the data. It would save them time, and save our servers.

The javascript was pretty involved. It had to generate the same HTML as we did for the pivot table, which meant it had to know (or make up) values for each criterion, like all the case categories and sub-categories, and which sub-categories belonged to which categories. And we let the customers pivot on up to THREE, count ’em, different criteria. And it had to happen each time the user picked different pivot criteria. It took a few tricks, but we got it working. It ran slowly, maybe a few seconds, but it was quick enough, probably, especially once we threw in a little “please wait” spinner. Then we realized we needed to re-render whenever the window resized.

No biggie, I thought, and I quickly added window.onResize(reportPreview);. It worked great, except it re-rendered the report with every pixel-wide movement of the mouse, as the window was dragged to new widths and heights. Calling a function, one that runs for a few seconds, a hundred times in the time it took to widen the browser an inch, meant a locked browser. It meant “time to get more coffee,” and after, “time to fix the bug.”

I knew we could delay calling reportPreview, but we only wanted to delay it when the window was being resized — when the user changed the columns, there was no reason to wait. I was sure window.setTimeout() would do what we needed, but I didn’t want to muck up reportPreview() with it.

I’d been reading The Little Schemer lately, and noticing some striking similarities between javascript and scheme: first-class functions, and higher-order functions that take functions as arguments, and return other functions as values. It was fun reading, with its strange teacher-student dialog style. The material was better than brainteasers, and I knew it would make me a better programmer down the road, but I didn’t think of it as relevant to the day-job, as…applicable.

Then I realized higher-order functions were a way out of this. I could write a function, delay, that would wrap one long-running, slow-poke function in another function: an intermediary that you could call as many times a second as you wanted, but it would only call the slow-poke once things had settled down. delay would let us keep setTimeout out of reportPreview. Something like this:

function delay(millis, slowPoke) {
    var timeoutId = undefined;

    // This is the intermediary.  Call it lots, it won't hurt.
    return function() {
        if (timeoutId) {  // If we're waiting...
            clearTimeout(timeoutId); // re-start the clock.
        }
        timeoutId = window.setTimeout(slowPoke, millis);
    }
}

The first time you call the intermediary, it tells the window to call slowPoke after a bit, but every time you call it after that, it starts the clock over. It’s like when you’re in a five-minute time-out, and you start acting up after only three, so your mom says “Ok, buster, another five minutes.”

var fiveMinutes = 5 * 60 * 1000;
var screamAndShout = delay(fiveMinutes, function() {
    getBackToPlaying();
});

screamAndShout(); // Aw nuts, I'm in time-out.

// I'll be good for as long as I can, but...
screamAndShout(); // dang, five MORE minutes!

Once delay was in place, running reportPreview when the window was resized was no problem.

function reportPreview() {
    // recursion, DOM manipulation, insanity...
}
columnPicker.onChange(reportPreview);
window.onResize(delay(100, reportPreview));

After testing, we found that delaying it for 100 milliseconds made all the difference in the world.

Do you have a war story? Share it, and start the healing: tell a short one in the comments, or link to a longer one on your own damn blog.

Continuations: a warm-up

Continuations and continuation-passing style (CPS) are introduced in The Little Schemer, chapter 8, using collectors: functions that collect values, through being repeatedly redefined. It was a tough chapter for me, but the idea is simple once you get it, so I’d like to leave some help for others. I’ll use Ruby for the examples, with some JavaScript and Scheme at the end.

In languages with first-class functions, you can assign functions to variables, and re-assign those variables. Consider this Ruby example:

func = lambda { |x| puts x }

['a', 'b', 'c'].each { |ch|
    old_func = func
    func = lambda { |x| old_func[x + ch] }
}

func['d']  #=> prints 'dcba'

By re-defining func in terms of old_func, we’re building up a recursive computation. It’s like normal recursion, but approached from the other side — without explicit definitions. Since a Ruby function is a closure, it remembers the value of the variables in scope when it was created; each layer of this recursion holds its own value for ch and old_func. When we call the last func, it sees ch = ‘c’ and x = ‘d’. It concatenates them, and calls its version of old_func…which sees x = ‘dc’ and ch = ‘b’, concatenates them, and passes it to its old_func, and so on.In fact, if we wrote it like this, the execution of all those lambdas would be exactly the same:

func_puts = lambda { |x| puts x }
func_add_a = lambda { |x| func_puts[x + 'a'] }
func_add_b = lambda { |x| func_add_a[x + 'b'] }
func_add_c = lambda { |x| func_add_b[x + 'c'] }
func_add_c['d']  #=> prints 'dcba'

We could calculate factorials this way:

def factorial(n, func)
    if n == 1
        func[1]
    else
        factorial(n - 1, lambda { |i| func[i * n] })
    end
end
factorial(3, lambda { |fact| puts fact })  #=> prints 6
  1. On the first call to factorial, n = 3, and func just prints its argument. But func isn’t called yet…since n isn’t 1, we recurse with n - 1 = 2, and a new function that calls func with its argument i times 3.
  2. On the recurse, n = 2, and func calls the original printer func with its argument i times 3. Since n still isn’t 1, we recurse again, with n - 1 = 1, and a new function that calls our func with its argument i times 2.
  3. On the final round, n = 1, so we (finally!) call func with 1, which…
  4. …calls the previous func with 1 * 2, which…
  5. …calls the original func with (1 * 2) * 3, which…
  6. prints 6.

As factorial recurses, it builds up a recursive tower of func calls. In factorial‘s base case, the last (and outermost) func is called, and we begin to climb down the func tower, to its bottom floor, the original func. It’s recursion in two directions.

In case Ruby isn’t your favorite language, here are versions in JavaScript and Scheme:

function factorial(n, func) {
    if (n == 1)
        func(1)
    else
        factorial(n - 1, function(i) {
            func(n * i)
        });
}

factorial(3, function(fact) { print(fact) })
; Too bad WordPress doesn't format Scheme or Lisp!
(define factorial
  (lambda (n func)
    (cond ((= n 1) (func 1))
          (else (factorial (- n 1)
                           (lambda (i) (func (* n i))))))))

; Here, the original func is just an identity function.
(factorial 4 (lambda (fact) fact))

Once this is clear, you can see many other applications:

  • You could find all the even numbers in a list: instead of passing a number to func, pass the list of even numbers, and add each even number in.
  • You could separate the numbers into evens and odds: instead of passing just one list to func, pass two lists, for evens and odds, and add each number to the correct list.
  • You could separate any list of items by any criteria: instead of hard-coding “is even?” into the function, pass a predicate function. (Maybe you want to extract all occurrences of ‘tuna’ from a list of words.)

That should be enough of a warm-up for chapter 8. See you in chapter 9, when they derive the Y-combinator!

Why Functional JavaScript?

I’m teaching myself functional programming (FP). I first noticed it in Ruby, even though it’s been in JavaScript all along. I’m working my way through Structure and Interpretation of Computer Programs (SICP), and The Little Schemer books, so I’m learning Scheme and Lisp too.

When studying FP, I generally use JavaScript and Scheme. My FP examples here are usually in JavaScript:

  • It’s available everywhere, and most programmers are comfortable reading it.
  • It’s closer to Scheme than Ruby is, so the examples translate better. In fact, Douglas Crockford notes: “JavaScript has much in common with Scheme. It is a dynamic language. It has a flexible datatype (arrays) that can easily simulate s-expressions. And most importantly, functions are lambdas. Because of this deep similarity, all of the functions in The Little Schemer can be written in JavaScript.”

I include Ruby or Scheme, though, if it seems appropriate.

I often use the JavaScript Shell, or its big brother, the JavaScript Development environment, because I haven’t found or built a JavaScript REPL as nice as Dr. Scheme.

Performance

Most current JavaScript implementations are slow with recursion and closures…two cornerstones of functional programming.

I don’t worry about this, because my examples are not meant to be dropped into production systems without thought and testing. I write production-ready code at work; here, I play and explore. We do use some functional JavaScript where I work, but it’s where the performance is acceptable, and the imperative alternative would be too unwieldly.

History seems to show that performance is a short-term concern, and better programming techniques are a long-term concern. It also seems that there are no inherent reasons JavaScript is slow; more performant implementations may be in our future.

Why not Haskell or Erlang?

…or OCaml, Scala, F#, Clojure? I’m getting there. SICP and The Little Schemer books are more than enough for me right now.