elm-lang/core — pull request #25 by jwmerrill (2014-12-02 00:02:43+00:00)
Title: Make draw scheduling more robust.

The primary motivation here is to allow the draw scheduler to cooperate with
an external requestAnimationFrame loop, which users can implement via a port,
or using the experimental [Monitor][] package. Unlike fps, this makes it
possible to achieve 60 fps animation in Elm without regularly dropping frames.

[Monitor]: (https://github.com/jwmerrill/elm-animation-frame)

In the existing implementation, it is possible to block rendering forever
using an external requestAnimationFrame loop by scheduling a new domUpdate for
the beginning of each animation frame. This cancels the previous draw callback
(using cancelAnimationFrame), and schedules a new draw callback that will not
run until the *next* frame. If a new domUpdate is requested in the next frame
also, the new draw callback will also be cancelled, and the process can repeat
arbitrarily long until new domUpdates stop being requested.

The new implementation does not use cancelAnimationFrame. Instead, it uses a
small state machine to ensure that only one drawCallback is ever scheduled per
frame.

The new implementation also drops the vendor prefixes on requestAnimationFrame
because [no recent browser uses them][1]. In case requestAnimationFrame is not
available, it uses `setTimeout(cb, 1000/60)`. The existing implementation used
an immediate call, but I think it's better to throttle draw calls to make the
behavior more similar to the case where requestAnimationFrame is available.

[1]: (https://developer.mozilla.org/en-US/docs/Web/API/window.requestAnimationFrame#Browser_compatibility)

A final small advantage of the new implementation is that, unlike the existing
implementation, it does not create a new function closure on every draw call.