I’m pretty new to CoffeeScript/JavaScript.

I was recently working on a little CoffeeScript class that keeps track of pending asynchronous requests and then optionally performs an action once all outstanding requests have finished. The requests can start at any time, so grouping them all into a single when() isn’t an option. We extracted a simple Watcher class (names changed to protect the guilty) to handle the logic.

We started out with a simple counter to keep track of the requests. CoffeeScript veterans will already see a problem with this code, but bear with me.

Initial Version
class Watcher
pendingRequests: 0
onCompletion: (callback) ->
@completionCallback = callback
@maybeComplete()
requestStarted: ->
@pendingRequests++
requestFinished: ->
@pendingRequests-- if @pendingRequests > 0
requestFailed: ->
@completionCallback = null
maybeComplete: ->
if @pendingRequests is 0
@completionCallback?()
@completionCallback = null

We later realized that we needed to keep track of the actual requests and not just a count, so we modified the code to look more like this:

WRONG!
class Watcher
pendingRequests: []
onCompletion: (callback) ->
@completionCallback = callback
@maybeComplete()
requestStarted: (request) ->
@pendingRequests.push(request)
requestFinished: (request) ->
@pendingRequests = _(@pendingRequests).without(request)
requestFailed: ->
@completionCallback = null
@pendingRequests = []
maybeComplete: ->
if _(@pendingRequests).isEmpty()
@completionCallback?()
@completionCallback = null

We started getting all kinds of test failures after this. After digging in, it looked like the @pendingRequests array was not starting out empty for later tests. This confused us, because we were clearly initializing it to an empty array, right?

For a newbie like me, this code looked fine, but what’s really going on under the hood is that we were defining the pendingRequests variable on Watcher.prototype, not initializing an instance variable for each Watcher instance.

We didn’t see the problem with our initial implementation, because we weren’t destructively modifying the count like we were doing with the array.

This blog post gives a really nice overview of the different ways of defining CoffeeScript variables, showing the resulting JavaScript code.

The proper way to do what we were trying to do is to define a constructor and initialize the variable there:

RIGHT!
class Watcher
constructor: ->
@pendingRequests = []
onCompletion: (callback) ->
@completionCallback = callback
@maybeComplete()
requestStarted: (request) ->
@pendingRequests.push(request)
requestFinished: (request) ->
@pendingRequests = _(@pendingRequests).without(request)
requestFailed: ->
@completionCallback = null
@pendingRequests = []
maybeComplete: ->
if _(@pendingRequests).isEmpty()
@completionCallback?()
@completionCallback = null

Hopefully this helps another CoffeeScript newbie like me from falling into the same trap.