Tracker

Documentation of Tracker, Meteor's reactive system.

Meteor has a simple dependency tracking system which allows it toautomatically rerun templates and other computations wheneverSession variables, database queries, and other datasources change.

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

Applications will find Tracker.autorun useful, while moreadvanced facilities such as Tracker.Dependency and onInvalidatecallbacks are intended primarily for package authors implementing newreactive data sources.

Client

Tracker.autorun(runFunc, [options])

import { Tracker } from 'meteor/tracker' (tracker/tracker.js, line 569)

Run a function now and rerun it later whenever its dependencieschange. Returns a Computation object that can be used to stop or observe thererunning.

Arguments

  • runFuncFunction
  • The function to run. It receivesone argument: the Computation object that will be returned.

Options

  • onErrorFunction
  • Optional. The function to run when an errorhappens in the Computation. The only argument it receives is the Errorthrown. Defaults to the error being logged to the console.

Tracker.autorun allows you to run a function that depends on reactive datasources, in such a way that if there are changes to the data later,the function will be rerun.

For example, you can monitor a cursor (which is a reactive datasource) and aggregate it into a session variable:

  1. Tracker.autorun(() => {
  2. const oldest = _.max(Monkeys.find().fetch(), (monkey) => {
  3. return monkey.age;
  4. });
  5. if (oldest) {
  6. Session.set('oldest', oldest.name);
  7. }
  8. });

Or you can wait for a session variable to have a certain value, and dosomething the first time it does, calling stop on the computation toprevent further rerunning:

  1. Tracker.autorun((computation) => {
  2. if (!Session.equals('shouldAlert', true)) {
  3. return;
  4. }
  5. computation.stop();
  6. alert('Oh no!');
  7. });

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

A change to a data dependency does not cause an immediate rerun, butrather “invalidates” the computation, causing it to rerun the nexttime a flush occurs. A flush will occur automatically as soon asthe system is idle if there are invalidated computations. You canalso use Tracker.flush to cause an immediate flush ofall pending reruns.

If you nest calls to Tracker.autorun, then when the outer call stops orreruns, the inner call will stop automatically. Subscriptions andobservers are also automatically stopped when used as part of acomputation that is rerun, allowing new ones to be established. SeeMeteor.subscribe for more information aboutsubscriptions and reactivity.

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

Client

Tracker.flush()

import { Tracker } from 'meteor/tracker' (tracker/tracker.js, line 445)

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 isidle. This keeps things predictable — you can know that the DOMwon’t go changing out from under your code as it runs. It’s also oneof 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 Sessionvariable that will cause part of the user interface to rerender, thehandler can call flush to perform the rerender immediately and thenaccess the resulting DOM.

An automatic flush occurs whenever the system is idle which performsexactly the same work as Tracker.flush. The flushing process consistsof rerunning any invalidated computations. If additionalinvalidations happen while flushing, they are processed as part of thesame flush until there is no more work to be done. Callbacksregistered with Tracker.afterFlush are calledafter processing outstanding invalidations.

It is illegal to call flush from inside a flush or from a runningcomputation.

The Tracker manualdescribes the motivation for the flush cycle and the guarantees made byTracker.flush and Tracker.afterFlush.

Client

Tracker.nonreactive(func)

import { Tracker } from 'meteor/tracker' (tracker/tracker.js, line 599)

Run a function without tracking dependencies.

Arguments

  • funcFunction
  • A function to call immediately.

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

Client

Tracker.active

import { Tracker } from 'meteor/tracker' (tracker/tracker.js, line 24)

True if there is a current computation, meaning that dependencies on reactive data sources will be tracked and potentially cause the current computation to be rerun.

This value is useful for data source implementations to determinewhether they are being accessed reactively or not.

Client

Tracker.inFlush()

import { Tracker } from 'meteor/tracker' (tracker/tracker.js, line 455)

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

import { Tracker } from 'meteor/tracker' (tracker/tracker.js, line 33)

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. Thecurrent computation is used implicitly byTracker.active (which tests whether there is one),dependency.depend() (which registers that it depends on adependency), and Tracker.onInvalidate (whichregisters a callback with it).

Client

Tracker.onInvalidate(callback)

import { Tracker } from 'meteor/tracker' (tracker/tracker.js, line 616)

Registers a new onInvalidate callback on the current computation (which must exist), to be called immediately when the current computation is invalidated or stopped.

Arguments

  • callbackFunction
  • A callback function that will be invoked as func(c), where c is the computation on which the callback is registered.

See computation.onInvalidate for moredetails.

Client

Tracker.afterFlush(callback)

import { Tracker } from 'meteor/tracker' (tracker/tracker.js, line 630)

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 guaranteedto run in the order that afterFlush was called. Functions areguaranteed to be called at a time when there are no invalidatedcomputations that need rerunning. This means that if an afterFlushfunction invalidates a computation, that computation will be rerunbefore any other afterFlush functions are called.

Tracker.Computation

A Computation object represents code that is repeatedly rerun inresponse to reactive data changes. Computations don’t have returnvalues; they just perform actions, such as rerendering a template onthe screen. Computations are created using Tracker.autorun.Use stop to prevent further rerunning of acomputation.

Each time a computation runs, it may access various reactive datasources that serve as inputs to the computation, which are called itsdependencies. At some future time, one of these dependencies maytrigger the computation to be rerun by invalidating it. When thishappens, the dependencies are cleared, and the computation isscheduled to be rerun at flush time.

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

Invalidating a computation sets its invalidated property to trueand immediately calls all of the computation’s onInvalidatecallbacks. When a flush occurs, if the computation has been invalidatedand not stopped, then the computation is rerun by setting theinvalidated property to false and calling the original functionthat was passed to Tracker.autorun. A flush will occur when the currentcode finishes running, or sooner if Tracker.flush is called.

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

Example:

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

Client

Tracker.Computation#stop()

(tracker/tracker.js, line 287)

Prevents this computation from rerunning.

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

Stopping a computation causes its onInvalidate callbacks to runimmediately if it is not currently invalidated, as well as itsstop callbacks.

Nested computations are stopped automatically when their enclosingcomputation is rerun.

Client

Tracker.Computation#invalidate()

(tracker/tracker.js, line 259)

Invalidates this computation so that it will be rerun.

Invalidating a computation marks it to be rerun atflush time, atwhich point the computation becomes valid again. It is rare toinvalidate a computation manually, because reactive data sourcesinvalidate their calling computations when they change. Reactive datasources in turn perform this invalidation using one or moreTracker.Dependency objects.

Invalidating a computation immediately calls all onInvalidatecallbacks registered on it. Invalidating a computation that iscurrently invalidated or is stopped has no effect. A computation caninvalidate itself, but if it continues to do so indefinitely, theresult will be an infinite loop.

Client

Tracker.Computation#onInvalidate(callback)

(tracker/tracker.js, line 222)

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 firesimmediately or as soon as the computation is next invalidated orstopped. It is used by reactive data sources to clean up resources orbreak dependencies when a computation is rerun or stopped.

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

Client

Tracker.Computation#onStop(callback)

(tracker/tracker.js, line 240)

Registers callback to run when this computation is stopped, or runs it immediately if the computation is already stopped. The callback is run after any onInvalidate callbacks.

Arguments

  • callbackFunction
  • Function to be called on stop. Receives one argument, the computation that was stopped.

Client

Tracker.Computation#stopped

(tracker/tracker.js, line 161)

True if this computation has been stopped.

Client

Tracker.Computation#invalidated

(tracker/tracker.js, line 172)

True if this computation has been invalidated (and not yet rerun), or if it has been stopped.

This property is initially false. It is set to true by stop() andinvalidate(). It is reset to false when the computation isrecomputed at flush time.

Client

Tracker.Computation#firstRun

(tracker/tracker.js, line 184)

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 acomputation has logic specific to the first run.

Tracker.Dependency

A Dependency represents an atomic unit of reactive data that acomputation might depend on. Reactive data sources such as Session orMinimongo internally create different Dependency objects for differentpieces of data, each of which may be depended on by multiplecomputations. When the data changes, the computations areinvalidated.

Dependencies don’t store data, they just track the set of computations toinvalidate if something changes. Typically, a data value will beaccompanied by a Dependency object that tracks the computations that dependon it, as in this example:

  1. let weather = 'sunny';
  2. const weatherDep = new Tracker.Dependency;
  3. function getWeather() {
  4. weatherDep.depend();
  5. return weather;
  6. }
  7. function setWeather(newWeather) {
  8. weather = newWeather;
  9. // Note: We could add logic here to only call `changed` if the new value is
  10. // different from the old value.
  11. weatherDep.changed();
  12. }

This example implements a weather data source with a simple getter andsetter. The getter records that the current computation depends onthe weatherDep dependency using depend(), while the settersignals the dependency to invalidate all dependent computations bycalling changed().

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

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

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

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

Client

Tracker.Dependency#changed()

(tracker/tracker.js, line 420)

Invalidate all dependent computations immediately and remove them as dependents.

Client

Tracker.Dependency#depend([fromComputation])

(tracker/tracker.js, line 396)

Declares that the current computation (or fromComputation if given) depends on dependency. The computation will be invalidated the next time dependency changes.

If there is no current computation and depend() is called with no arguments, it does nothing and returns false.

Returns true if the computation is a new dependent of dependency rather than an existing one.

Arguments

  • fromComputationTracker.Computation
  • An optional computation declared to depend on dependency instead of the current computation.

dep.depend() is used in reactive data source implementations to recordthe fact that dep is being accessed from the current computation.

Client

Tracker.Dependency#hasDependents()

(tracker/tracker.js, line 432)

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 isstill tracking any dependency relationships or if it can be cleaned upto save memory.