Runtime: Unobserve after calling module.value()

To fetch a single value from a Runtime module, one has to call

module.value(name)

Looking at the source, this will set variable._observer to true:

It is my understanding that this flag is never cleared afterwards, meaning that the variable will continue to be considered reachable and thus computed:

Question 1: Should I instead derive a variable and delete it afterwards, to ensure that computation will happen only once?

Question 2: To my knowledge values can only be queried for named variables (if we ignore the internal API). If derive/delete is the way to go, this leaves me with two options:

  1. use a randomized name:
    (name, module) => {
      const _name = `${name}_${Date.now()}_${Math.random() * 1e8 | 0}`;
      const variable = module.variable().define(_name, [name], v => v);
      const promise = module.value(_name);
      promise.finally(() => variable.delete());
      return promise;
    };
    
  2. Set a custom observer:
    (name, module) => {
      let variable;
      const promise = new Promise((fulfilled, rejected) => {
        variable = module.variable({fulfilled, rejected}).define([name], v => v);
      });
      promise.finally(() => variable.delete());
      return promise;
    }
    

Option 1 feels like a hack, so I’m leaning towards option 2. (Interestingly, deleting the variable will invoke the observer’s fulfilled callback a second time, with a value of undefined.)

What side effects or overhead should I be aware of? Are there other alternatives?

2 Likes

Are you creating the module yourself in this scenario? I’m curious how you’re using the runtime here. Typically most variables are already observed, so I confess I haven’t thought too much about when this variable should be unobserved. And also module.value is really just intended as a convenience method for when you don’t want to create a bona fide observer yourself, and mostly assumes the variable only has a single value since you’re only awaiting the first value.

So, I think (2) is your best bet. Invoking the observer’s fulfilled a second time is mostly intended, although perhaps it should reject instead of fulfilling with undefined.

You’re welcome to open a pull request to the runtime if you’d prefer some different behavior. Maybe we would need to do some reference counting in module_value? Or more likely, perhaps module_value should create a derived variable that is observed and then delete it after the promise resolves or rejects? Suggestions welcome.

Thanks for the reply and additional background, Mike!

I’m implementing a dynamic import helper for notebooks. Instead of defining an observer upfront, observers are attached on demand when cells are retrieved within the importing notebook:

I’ll have to dig a bit deeper into the Runtime API first. Right now I can’t think of anything that I feel is worth suggesting.

We’ve updated module.value to clean up after itself.

2 Likes