An Inadvertent Caching Bug

Here's some CoffeeScript I wrote in a fit of refactoring a few months ago. It's an AngularJS factory that does geolocation and returns a promise. In the heat of some intense copy and pasting, I missed a super important detail. Can you see what it is?

angular.module('app')  
.factory "geolocation", ($q, $ionicPlatform, $cordovaGeolocation) ->
  geocode = $q (resolve, reject) ->
    console.log "hello!"
    # ...

return {  
  get: ->
    return geocode
}

The detail reared it's head when Ionic, the mobile framework we use, turned on caching by default in an update. Suddenly we had to pay a lot more attention to bugs that might occur on repeated user actions. We discovered that clicking a "change my zip code" button didn't actually change your zip code if you used it twice.

First I debugged caching on the page itself. Then I thought that Ionic might somehow be caching factory results. It wasn't either of those things. I was somewhat baffled that hello! was only printed the first time that the get method was executed.

Here's the slightly more correct version:

angular.module('app')  
.factory "geolocation", ($q, $ionicPlatform, $cordovaGeolocation) ->
  geocode = ->
    return $q (resolve, reject) ->
      console.log "hello!"
      # ...

return {  
  get: geocode
}

See the difference? AngularJS factories are singletons. There's only one copy of the factory at runtime, and it's persistent, even if you reload the page. The original geocode method wasn't a method at all; it was a promise. Every time I reloaded the page, the exact same instance of the promise object was returned. Apparently when you call the same promise multiple times you get the same result, at least with the $q library used by Angular.

It's this rich interplay of emergent properties that makes debugging my favorite activ- oh who am I kidding. Factories are the Angular equivalent of global variables; use them wisely, save yourself pain.