on master help sign in
Fix "dropped" frames for text updates (#224, #339) using @timthelion's insights in #350

* Change the implementation of `notify` so that it does not have a return value any more. * Get rid of the only remaning use of the return value of `notify` in the `every` function. * Have a "lock" on each round to ensure that no library or runtime calls `notify` synchronously, causing an update to happen half way through another update, ultimately leading to a dropped frame.

commit704da021b074a3e51bdcd8425870ccc5d7fdedf3 authorEvan Czaplicki <info@elm-lang.org> dateMon 18 Nov 2013, 17:22 (13 years ago) parenta49d381
3 files changed+27−12
libraries/Native/Signal/Time.js+5−7 libraries/Native/Signal/Window.js+12−1 runtime/Init.js+10−4
libraries/Native/Signal/Time.js+5−7
@@ -34,13 +34,11 @@ Elm.Native.Time.make = function(elm) {
3434 return A3( Signal.lift2, F2(f), isOn, ticker );
3535 }
3636
37− function everyWhen(t, isOn) {
37+ function every(t) {
3838 var clock = Signal.constant(Date.now());
39− var id = setInterval(function tellTime() {
40− if (!elm.notify(clock.id, Date.now())) {
41− clearInterval(id);
42− }
43− }, t);
39+ setInterval(function() {
40+ elm.notify(clock.id, Date.now());
41+ }, t);
4442 return clock;
4543 }
4644
@@ -56,7 +54,7 @@ Elm.Native.Time.make = function(elm) {
5654 return elm.Native.Time.values = {
5755 fpsWhen : F2(fpsWhen),
5856 fps : function(t) { return fpsWhen(t, Signal.constant(true)); },
59− every : function(t) { return everyWhen(t, Signal.constant(true)) },
57+ every : every,
6058 delay : NS.delay,
6159 timestamp : NS.timestamp,
6260 since : F2(since),
libraries/Native/Signal/Window.js+12−1
@@ -28,10 +28,21 @@ Elm.Native.Window.make = function(elm) {
2828 height.defaultNumberOfKids = 0;
2929
3030 function resizeIfNeeded() {
31+ // Do not trigger event if the dimensions have not changed.
32+ // This should be most of the time.
3133 var w = getWidth();
3234 var h = getHeight();
3335 if (dimensions.value._0 === w && dimensions.value._1 === h) return;
34− elm.notify(dimensions.id, Tuple2(w,h));
36+
37+ setTimeout(function () {
38+ // Check again to see if the dimensions have changed.
39+ // It is conceivable that the dimensions have changed
40+ // again while some other event was being processed.
41+ var w = getWidth();
42+ var h = getHeight();
43+ if (dimensions.value._0 === w && dimensions.value._1 === h) return;
44+ elm.notify(dimensions.id, Tuple2(w,h));
45+ }, 0);
3546 }
3647 elm.addListener([dimensions.id], window, 'resize', resizeIfNeeded);
3748
runtime/Init.js+10−4
@@ -31,14 +31,20 @@ function init(display, container, module, moduleToReplace) {
3131 // defining state needed for an instance of the Elm RTS
3232 var inputs = [];
3333
34+ var updateInProgress = false;
3435 function notify(id, v) {
36+ if (updateInProgress) {
37+ throw new Error(
38+ 'The notify function has been called synchronously!\n' +
39+ 'This can lead to frames being dropped.\n' +
40+ 'Definitely report this to <https://github.com/evancz/Elm/issues>\n');
41+ }
42+ updateInProgress = true;
3543 var timestep = Date.now();
36− var changed = false;
3744 for (var i = inputs.length; i--; ) {
38− // order is important here to avoid short-circuiting
39− changed = inputs[i].recv(timestep, id, v) || changed;
45+ inputs[i].recv(timestep, id, v);
4046 }
41− return changed;
47+ updateInProgress = false;
4248 }
4349
4450 var listeners = [];