angular/angular.js — pull request #3570 by andyrooger (2013-08-14 00:55:11+00:00)
Title: fix($timeout): clean deferreds immediately after callback exec/cancel

Make sure $timeout callbacks are forgotten about immediately after
execution or cancellation.

Previously when passing invokeApply=false, the cleanup used $q and so
would be pending until the next $digest was triggered. This does not
make a large functional difference, but can be very visible when
looking at memory consumption of an app or debugging around the
$$asyncQueue - these callbacks can have a big retaining tree.

---

Motivation

In the app that I am working on, we have some quite memory hungry pages. I noticed that on navigating away from them to lighter pages, the memory they consumed would not be released until something was clicked or happened to cause a $digest.

After some digging it turns out that the form controller in the page is releasing event handlers inside a $timeout with applyInvoke=false. The callback is happening without trouble and releases the event handlers, but it is not itself released until the next digest cycle, and contains most of the page in its closure.

It is not clear from the $timeout documentation that internal cleanup should be dependant on the invokeApply parameter. I would only expect callbacks I'd added myself to the returned promise to be dependant on a digest as this is part of using $q. I don't think the changes in this pull request should be observable without debugging tools so shouldn't affect current code.