tangrams/tangram — pull request #48 by bcamper (2014-10-27 00:37:53+00:00)
Title: Use throttle-with-single-entry-queue strategy for re-parsing styles & re-compiling GL programs

We only want one call to ```Scene.updateModes()``` (previously ```refreshModes()```) to be running at any given time. This method has multiple levels of asynchronous calls: a first loop over each render mode, with an async call to refresh each; then within each of those calls, multiple GL programs may be compiled, and each of *those* calls is also async (or at least can be, as the GL compile call assembles the final shader source, and some shader transforms may be retrieved from remote sources via XHR). ```updateModes()``` waits for all of these processes to return before considering itself done calling its final callback. In addition, we want the state of the stylesheet locked during these operations, to ensure consistency across the different modes (they all need to share the same camera transforms, for example, so it wouldn't do to have that changed mid-compilation). So ```updateModes()``` does not allow itself to be invoked if a previous call is already in progress (this is enforced via the ```this.compiling``` variable, which also stores the callback (if any) to call when the entire operation is complete).

However, it is also a common use to call ```updateModes()``` frequently, for example in response to a GUI slider control that changes parameters in the stylesheet. To accommodate this, ```updateModes()`` also allows *up to one* additional call request to be queued (stored in ```this.compiling.queued```), and it will invoke itself again in these cases when execution finishes. If a request to ```updateModes()``` is made and another request has *already* been queued, the previous queued request is replaced (its callback is called with a value to indicate it has been superseded), and the new queued request takes its place; so only one future request is allowed to be queued.

The same throttle-with-single-entry-queue strategy is also employed for the ```Scene.rebuildGeometry()``` method, which is also asynchronous with many moving parts (though considerably different, as that method must make multiple requests that are fulfilled by web workers). In the future we may consolidate the throttle/queue mechanism, but for now the two cases are different enough that it may not be the most effective route.