Elegant Function Timer Method Decorator in CoffeeScript

CoffeeScript is so badass. Given its transpiling and symbiotic nature, we can borrow our favorite conventions from our favorite languages and let the ugly, optimized JavaScript runtime version get compiled behind the scenes.

This is great news for people like myself who entered the programming game via Ruby (and/or were spoiled by it) and now struggle to efficiently read and write code if it is not simple and concise and has that human-readable touch. In today’s landscape of endless mountains of JavaScript, any help to mask its shortcomings and break out of “callback hell” is a welcomed improvement to my workflow and code structure.

Method Decorators

In this vain, I have struggled with the lack of a good pattern for decorating methods – doing things before, after, or around functions. For the Rubyist on Rails, you can think of the handiness of ActionController’s filter methods and ActiveRecord’s callbacks. But alas, JavaScript makes it syntactically tricky with its function scoping and closures to properly pass arguments and callback functions around in a peaceful manner.

Then I discovered Reginald Braithwaite’s awesome method decorators and combinators:

method-decorators.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
this.before =
  (decoration) ->
    (base) ->
      ->
        decoration.apply(this, arguments)
        base.apply(this, arguments)

this.after =
  (decoration) ->
    (base) ->
      ->
        decoration.call(this, __value__ = base.apply(this, arguments))
        __value__

this.around =
  (decoration) ->
    (base) ->
      (argv...) ->
        __value__ = undefined
        callback = =>
          __value__ = base.apply(this, argv)
        decoration.apply(this, [callback].concat(argv))
        __value__

Definitely read Reginald’s post for more of an overview on these. But for now, onto the practical example.

The Timer

I’ve been mainly working on native-wrapped HTML5 mobile apps lately and I wanted a way to easily profile any function and log the milliseconds it takes to execute. It turns out I was able to do this as a small extension to this.around.

1
2
3
4
5
6
@timer = (name) ->
  around (fn) ->
    t = new Date().getTime()
    fn()
    ms = new Date().getTime() - t
    console.log("#{name} - #{ms.toString()}ms")

Give the timer a name and it applies an around filter to your returned function, which checks the difference between the milliseconds before and after the function call and prints it to the log. Your expected value of this and any named arguments will be maintained in your decorated method. For example, say you wanted to profile how long it takes a Backbone View to render.

dashboard.coffee
1
2
3
4
5
6
7
class Dashboard extends Backbone.View
  # ...
  render: timer("dashboard render") (collection) ->
    @$el.html @template items: (collection or @collection).toJSON()
    @

# example console log: dashboard render - 24ms

Easy peasy. This doesn’t account for deferred events like ajax callbacks, but it does let me analyze the client-side operations that I’m most interested in optimizing.

Comments