Tracker

Meteor has a simple dependency tracking system which allows it to automatically rerun templates and other computations whenever Session variables, database queries, and other data sources change.

Unlike most other systems, you don’t have to manually declare these dependencies — it “just works”. The mechanism is simple and efficient. When you call a function that supports reactive updates (such as a database query), it automatically saves the current Computation object, if any (representing, for example, the current template being rendered). Later, when the data changes, the function can “invalidate” the Computation, causing it to rerun (rerendering the template).

Applications will find Tracker.autorun useful, while more advanced facilities such as Tracker.Dependency and onInvalidate callbacks are intended primarily for package authors implementing new reactive data sources.

The function is invoked immediately, at which point it may alert and stop right away if shouldAlert is already true. If not, the function is run again when shouldAlert becomes true.

A change to a data dependency does not cause an immediate rerun, but rather “invalidates” the computation, causing it to rerun the next time a flush occurs. A flush will occur automatically as soon as the system is idle if there are invalidated computations. You can also use Tracker.flush to cause an immediate flush of all pending reruns.

If you nest calls to Tracker.autorun, then when the outer call stops or reruns, the inner call will stop automatically. Subscriptions and observers are also automatically stopped when used as part of a computation that is rerun, allowing new ones to be established. See Meteor.subscribe for more information about subscriptions and reactivity.

If the initial run of an autorun throws an exception, the computation is automatically stopped and won’t be rerun.

Client

Tracker.flush()

Process all reactive updates immediately and ensure that all invalidated computations are rerun.

Normally, when you make changes (like writing to the database), their impact (like updating the DOM) is delayed until the system is idle. This keeps things predictable — you can know that the DOM won’t go changing out from under your code as it runs. It’s also one of the things that makes Meteor fast.

Tracker.flush forces all of the pending reactive updates to complete. For example, if an event handler changes a Session variable that will cause part of the user interface to rerender, the handler can call flush to perform the rerender immediately and then access the resulting DOM.

An automatic flush occurs whenever the system is idle which performs exactly the same work as Tracker.flush. The flushing process consists of rerunning any invalidated computations. If additional invalidations happen while flushing, they are processed as part of the same flush until there is no more work to be done. Callbacks registered with Tracker.afterFlush are called after processing outstanding invalidations.

It is illegal to call flush from inside a flush or from a running computation.

The Tracker manual describes the motivation for the flush cycle and the guarantees made by Tracker.flush and Tracker.afterFlush.

Client

Tracker.nonreactive(func)

Arguments

funcFunction

A function to call immediately.

Calls func with Tracker.currentComputation temporarily set to null and returns func‘s own return value. If func accesses reactive data sources, these data sources will never cause a rerun of the enclosing computation.

Client

Tracker.inFlush()

True if we are computing a computation now, either first time or recompute. This matches Tracker.active unless we are inside Tracker.nonreactive, which nullfies currentComputation even though an enclosing computation may still be running.

This value indicates, whether a flush is in progress or not.

Client

Tracker.currentComputation

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.

It’s very rare to need to access currentComputation directly. The current computation is used implicitly by Tracker.active (which tests whether there is one), dependency.depend() (which registers that it depends on a dependency), and Tracker.onInvalidate (which registers a callback with it).

Client

Tracker.afterFlush(callback)

Schedules a function to be called during the next flush, or later in the current flush if one is in progress, after all invalidated computations have been rerun. The function will be run once and not on subsequent flushes unless afterFlush is called again.

Arguments

callbackFunction

A function to call at flush time.

Functions scheduled by multiple calls to afterFlush are guaranteed to run in the order that afterFlush was called. Functions are guaranteed to be called at a time when there are no invalidated computations that need rerunning. This means that if an afterFlush function invalidates a computation, that computation will be rerun before any other afterFlush functions are called.

Tracker.Computation

A Computation object represents code that is repeatedly rerun in response to reactive data changes. Computations don’t have return values; they just perform actions, such as rerendering a template on the screen. Computations are created using Tracker.autorun. Use stop to prevent further rerunning of a computation.

Each time a computation runs, it may access various reactive data sources that serve as inputs to the computation, which are called its dependencies. At some future time, one of these dependencies may trigger the computation to be rerun by invalidating it. When this happens, the dependencies are cleared, and the computation is scheduled to be rerun at flush time.

The current computation (Tracker.currentComputation) is the computation that is currently being run or rerun (computed), and the one that gains a dependency when a reactive data source is accessed. Data sources are responsible for tracking these dependencies using Tracker.Dependency objects.

Invalidating a computation sets its invalidated property to true and immediately calls all of the computation’s onInvalidate callbacks. When a flush occurs, if the computation has been invalidated and not stopped, then the computation is rerun by setting the invalidated property to false and calling the original function that was passed to Tracker.autorun. A flush will occur when the current code finishes running, or sooner if Tracker.flush is called.

Stopping a computation invalidates it (if it is valid) for the purpose of calling callbacks, but ensures that it will never be rerun.

Example:

// If we're in a computation, then perform some clean-up when the current
// computation is invalidated (rerun or stopped).
if (Tracker.active) {
Tracker.onInvalidate(() => {
x.destroy();
y.finalize();
});
}

Client

Tracker.Computation#stop()

Stopping a computation is irreversible and guarantees that it will never be rerun. You can stop a computation at any time, including from the computation’s own run function. Stopping a computation that is already stopped has no effect.

Stopping a computation causes its onInvalidate callbacks to run immediately if it is not currently invalidated, as well as its stop callbacks.

Nested computations are stopped automatically when their enclosing computation is rerun.

Client

Tracker.Computation#invalidate()

Invalidating a computation marks it to be rerun at flush time, at which point the computation becomes valid again. It is rare to invalidate a computation manually, because reactive data sources invalidate their calling computations when they change. Reactive data sources in turn perform this invalidation using one or more Tracker.Dependency objects.

Invalidating a computation immediately calls all onInvalidate callbacks registered on it. Invalidating a computation that is currently invalidated or is stopped has no effect. A computation can invalidate itself, but if it continues to do so indefinitely, the result will be an infinite loop.

Client

Tracker.Computation#onInvalidate(callback)

Registers callback to run when this computation is next invalidated, or runs it immediately if the computation is already invalidated. The callback is run exactly once and not upon future invalidations unless onInvalidate is called again after the computation becomes valid again.

Arguments

callbackFunction

Function to be called on invalidation. Receives one argument, the computation that was invalidated.

onInvalidate registers a one-time callback that either fires immediately or as soon as the computation is next invalidated or stopped. It is used by reactive data sources to clean up resources or break dependencies when a computation is rerun or stopped.

To get a callback after a computation has been recomputed, you can call Tracker.afterFlush from onInvalidate.

Client

Tracker.Computation#firstRun

True during the initial run of the computation at the time Tracker.autorun is called, and false on subsequent reruns and at other times.

This property is a convenience to support the common pattern where a computation has logic specific to the first run.

Tracker.Dependency

A Dependency represents an atomic unit of reactive data that a computation might depend on. Reactive data sources such as Session or Minimongo internally create different Dependency objects for different pieces of data, each of which may be depended on by multiple computations. When the data changes, the computations are invalidated.

Dependencies don’t store data, they just track the set of computations to invalidate if something changes. Typically, a data value will be accompanied by a Dependency object that tracks the computations that depend on it, as in this example:

This example implements a weather data source with a simple getter and setter. The getter records that the current computation depends on the weatherDep dependency using depend(), while the setter signals the dependency to invalidate all dependent computations by calling changed().

The reason Dependencies do not store data themselves is that it can be useful to associate multiple Dependencies with the same piece of data. For example, one Dependency might represent the result of a database query, while another might represent just the number of documents in the result. A Dependency could represent whether the weather is sunny or not, or whether the temperature is above freezing. Session.equals is implemented this way for efficiency. When you call Session.equals('weather', 'sunny'), the current computation is made to depend on an internal Dependency that does not change if the weather goes from, say, rainy to cloudy.

Conceptually, the only two things a Dependency can do are gain a dependent and change.

A Dependency’s dependent computations are always valid (they have invalidated === false). If a dependent is invalidated at any time, either by the Dependency itself or some other way, it is immediately removed.

See the Tracker manual to learn how to create a reactive data source using Tracker.Dependency.

Client

Tracker.Dependency#hasDependents()

True if this Dependency has one or more dependent Computations, which would be invalidated if this Dependency were to change.

For reactive data sources that create many internal Dependencies, this function is useful to determine whether a particular Dependency is still tracking any dependency relationships or if it can be cleaned up to save memory.