diff --git a/packages/deps/deps.js b/packages/deps/deps.js index 87b6e8ccd2..5f33a5c55a 100644 --- a/packages/deps/deps.js +++ b/packages/deps/deps.js @@ -78,8 +78,9 @@ invalidate: function () { if (! this.invalidated) { if (! this.active) - // an active computation is enqueued at - // end of _run instead. + // an active computation is either already being + // processed or, on first run, is enqueued at + // the end of _run this._enqueue(); this.invalidated = true; } @@ -113,7 +114,7 @@ Deps.active = !! Deps.currentComputation; } - if (self.invalidated) + if (self.firstRun && self.invalidated) self._enqueue(); }, @@ -135,7 +136,8 @@ } } - if (! self.stopped) { + var wasStopped = self.stopped; + if (! wasStopped) { try { self._run(); } catch (e) { @@ -152,13 +154,16 @@ } } - if (self.stopped) + if (wasStopped) break; - // If we're not stopped but we are invalidated, also loop. - // It's valid in some cases for a computation to invalidate - // itself (or for afterInvalidate to invalid it), but we - // could add a run-away loop counter here. + // If _run() stopped us, we loop around to handle callbacks. + // If _run() invalidated us, we run again immediately. + // A computation that invalidates itself indefinitely is an + // infinite loop, of course. + // + // We could put an iteration counter here and catch run-away + // loops. } } }); diff --git a/packages/deps/deps_tests.js b/packages/deps/deps_tests.js index 95475b00cb..7f38f863da 100644 --- a/packages/deps/deps_tests.js +++ b/packages/deps/deps_tests.js @@ -246,6 +246,8 @@ Tinytest.add("deps - lifecycle", function (test) { }; }; + var shouldStop = false; + var c1 = Deps.run(function (c) { test.isTrue(Deps.active); test.equal(c, Deps.currentComputation); @@ -268,6 +270,9 @@ Tinytest.add("deps - lifecycle", function (test) { Deps.afterInvalidate(makeCb()); }); runCount++; + + if (shouldStop) + c.stop(); }); test.throws(function () { @@ -293,5 +298,21 @@ Tinytest.add("deps - lifecycle", function (test) { test.equal(runCount, 2); test.equal(c1.invalidated, false); + // 5/6, 11/12, etc. are from the nested run, whose + // invalidation is scheduled each time by the outer + // rerun. + // 1/3 are onInvalidate and 2/4 are afterInvalidate. test.equal(buf, [5, 6, 1, 3, 2, 4, 11, 12]); + + // test self-stop + buf.length = 0; + shouldStop = true; + c1.invalidate(); + Deps.flush(); + // when the computation stops itself, all the + // callbacks from last time and this time should + // get called consecutively, followed by the inner + // computation's 17/18. + test.equal(buf, [7, 9, 8, 10, 13, 15, 14, 16, 17, 18]); + }); \ No newline at end of file