Challenges with ReactiveVar in async autorun

Hi there,

it seems that you cannot register a reactiveVar (reactiveVar.get()) after an await in an autorun, i.e. in a Promise.then block.

const reactiveVar = new ReactiveVar("hi")

Template.hello.onCreated(function helloOnCreated() {
  Meteor.setTimeout(() => reactiveVar.set("ho"), 4000)

  this.autorun(async (c) => {
    const test = await Tracker.withComputation(c, () => Promise.resolve("hello"))
    console.log(test)
    const _reactiveVar = reactiveVar.get()
    console.log(_reactiveVar)
  })
});

console output:
hello
hi

“ho” never gets displayed.

Question:
Is this because the reactiveVar gets tracked in a Promise callback?

The reactiveVar is in the context, which is on the microTask queue at first. There is already “reactiveVar.set()” on the Task Queue. But as I understood, the microtask queue will be called before the Task Queue. So reactiveVar.get() gets called before and therefore the change (reactiveVar.set()) in the setTimeout should work.

But maybe my assumptions are wrong. Can we discuss it here? :smile:

The point is that Tracker.withComputation is needed only when your async function you await needs to be reactive and when you want to rely on the computation after first await. You can check the Tracker.withComputation tests here.

That means your code could look as follows:

this.autorun(async computation => {
  // No need to `Tracker.withComputation` here, as `Promise.resolve`
  // is not reactive source nor relies on one.
  const test = await Promise.resolve("hello");
  console.log(test);

  // Here we do, as we want the nested code to use a reactive source.
  Tracker.withComputation(computation, () => {
    const _reactiveVar = reactiveVar.get();
    console.log(_reactiveVar);
  });
})

Unfortunately it does not automatically fill-in the gaps “between awaits”.

EDIT: That’s also what the docs say:

In general, the rules to use Tracker.withComputation like this:

  1. async function before the first await if just like a sync one.
  2. async function after the first await looses the Tracker.currentComputation but it can be restored using Tracker.withComputation, but only within the callback.
1 Like

Just adding up as well, we have these docs for react packages, and in this section we comment about how to user .withComputation.

A rule of thumb is that if you are using a reactive function for example find + fetchAsync , it is nice to wrap it inside Tracker.withComputation to make sure that the computation is kept alive, if you are just calling that function that is not necessary, like the one bellow, will be always reactive.

1 Like