diff --git a/packages/tracker/package.js b/packages/tracker/package.js index 4448d883837..48563b20176 100644 --- a/packages/tracker/package.js +++ b/packages/tracker/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Dependency tracker to allow reactive callbacks", - version: "1.2.1" + version: "1.3.4" }); Package.onUse(function (api) { diff --git a/packages/tracker/tracker.d.ts b/packages/tracker/tracker.d.ts index 9ab6e0bdc52..5932be81b98 100644 --- a/packages/tracker/tracker.d.ts +++ b/packages/tracker/tracker.d.ts @@ -16,6 +16,10 @@ export namespace Tracker { * True during the initial run of the computation at the time `Tracker.autorun` is called, and false on subsequent reruns and at other times. */ firstRun: boolean; + /** + * Forces autorun blocks to be executed in synchronous-looking order by storing the value autorun promise thus making it awaitable. + */ + firstRunPromise: Promise /** * Invalidates this computation so that it will be rerun. */ @@ -48,7 +52,7 @@ export namespace Tracker { * The current computation, or `null` if there isn't one. The current computation is the `Tracker.Computation` object created by the innermost active call to * `Tracker.autorun`, and it's the computation that gains dependencies when reactive data sources are accessed. */ - var currentComputation: Computation; + var currentComputation: Computation | null; var Dependency: DependencyStatic; /** @@ -109,6 +113,16 @@ export namespace Tracker { } ): Computation; + /** + * @summary Helper function to make the tracker work with promises. + * @param computation Computation that tracked + * @param func a function that needs to be called and be reactive. This may be async or not - but this function is typically called after an await + */ + function withComputation( + computation: Computation | null, + func: () => T + ): T; + /** * Process all reactive updates immediately and ensure that all invalidated computations are rerun. */ diff --git a/packages/tracker/tracker.js b/packages/tracker/tracker.js index cb39f517415..2aaa968724b 100644 --- a/packages/tracker/tracker.js +++ b/packages/tracker/tracker.js @@ -32,11 +32,6 @@ Tracker.active = false; */ Tracker.currentComputation = null; -function setCurrentComputation(c) { - Tracker.currentComputation = c; - Tracker.active = !! c; -} - function _debugFunc() { // We want this code to work without Meteor, and also without // "console" (which is technically non-standard and may be missing @@ -201,6 +196,16 @@ Tracker.Computation = class Computation { this._onError = onError; this._recomputing = false; + /** + * @summary Forces autorun blocks to be executed in synchronous-looking order by storing the value autorun promise thus making it awaitable. + * @locus Client + * @memberOf Tracker.Computation + * @instance + * @name firstRunPromise + * @returns {Promise} + */ + this.firstRunPromise = undefined; + var errored = true; try { this._compute(); @@ -212,6 +217,22 @@ Tracker.Computation = class Computation { } } + + /** + * Resolves the firstRunPromise with the result of the autorun function. + * @param {*} onResolved + * @param {*} onRejected + * @returns{Promise { + return withNoYieldsAllowed(this._func)(this); + }); + // We'll store the firstRunPromise on the computation so it can be awaited by the callers, but only + // during the first run. We don't want things to get mixed up. + if (this.firstRun) { + this.firstRunPromise = Promise.resolve(firstRunPromise); + } } finally { - setCurrentComputation(previous); inCompute = previousInCompute; } } @@ -566,15 +594,12 @@ Tracker._runFlush = function (options) { * thrown. Defaults to the error being logged to the console. * @returns {Tracker.Computation} */ -Tracker.autorun = function (f, options) { +Tracker.autorun = function (f, options = {}) { if (typeof f !== 'function') throw new Error('Tracker.autorun requires a function argument'); - options = options || {}; - constructingComputation = true; - var c = new Tracker.Computation( - f, Tracker.currentComputation, options.onError); + var c = new Tracker.Computation(f, Tracker.currentComputation, options.onError); if (Tracker.active) Tracker.onInvalidate(function () { @@ -597,12 +622,25 @@ Tracker.autorun = function (f, options) { * @param {Function} func A function to call immediately. */ Tracker.nonreactive = function (f) { - var previous = Tracker.currentComputation; - setCurrentComputation(null); + return Tracker.withComputation(null, f); +}; + +/** + * @summary Helper function to make the tracker work with promises. + * @param computation Computation that tracked + * @param func a function that needs to be called and be reactive. This may be async or not - but this function is typically called after an await + */ +Tracker.withComputation = function (computation, f) { + var previousComputation = Tracker.currentComputation; + + Tracker.currentComputation = computation; + Tracker.active = !!computation; + try { return f(); } finally { - setCurrentComputation(previous); + Tracker.currentComputation = previousComputation; + Tracker.active = !!previousComputation; } };