Inconsistent sharing of yielded values?

I’ve created a set of UI controls for running simulations:
Simulation Controls

For the most part it works how I intend, but there is one scenario that is behaving inconsistently:
Using the first demo on the page, move the Speed slider to the far right (i.e. maximum speed), and then start the simulation by clicking Start. Now click the Reset button a few times.

Each time Reset is clicked, the simulation should restart counting at 0. I find that it is inconsistent. Sometimes it restarts the counter and sometimes it doesn’t. If I slow down the simulation (move the Speed slider to the left), then Reset works more consistently.

Each time Reset is clicked, the cell with the simulation should get called with con having a value of {reset: true}. In my generator function control, as far as I can tell, yield is getting correctly called with form.value = {reset: true}. But that doesn’t always get propagated to the simulation cell.

I could be totally off on this, but my best guess is that this is happening because Generators.observe is lossy? Is that the problem? Is there a way for me to avoid this problem? When the user clicks Reset, the simulation should consistently respond.

Thanks,
Adam

Yes, Generators.observe is lossy. You could use Generators.queue instead, but I’m not entirely sure that it would fix your problem. (Generators.input, which is used by viewof, uses Generators.observe internally, so you can’t really avoid it.)

What I would recommend here instead is that the value of your view be the cycle number, and to shun side effects and mutation as much as possible. If you need it, the value of your view could include a generation number in addition to the cycle number; the latter would increment whenever the simulation is reset. This way your downstream cells can be more tolerant of dropped events: they can advance the simulation when the cycle number increases, or reset the simulation if the generation number increases.

Another thing I will note is that your controls function is returning a generator. This isn’t generally necessary when implementing a view because you can update the view’s value merely by emitting an input event; you don’t also need to yield a new view. (The view hasn’t changed, only its value.) I can see why you might want a generator so that you can terminate the timer when the view is thrown away, but you could also do that by checking that the view is still attached to the DOM using isConnected. And there’s also Inputs.dispoal which is used by my similar Scrubber input. And Generators.disposable.

I would sketch out a more detailed example for you but I think the existing Scrubber input should hopefully give enough hints as to what I recommend.

2 Likes

Thanks for your incredibly quick and comprehensive response!

That’s what I suspected - since Generators.observe is baked into viewof, there’s no avoiding it if I want the elegance of viewof.

The downside of this is that the simulation then has to keep track of generation numbers to infer that a reset has been requested, instead of getting an explicit signal. I’ve come up with another approach, which seems to be working well: When a reset occurs, if the delay between steps is below some threshold, then just make the delay for that one step a bit longer. This avoids the reset signal getting swamped out by subsequent updates. Even 20 milliseconds seems to be sufficient to fix the problem.

Thank you for this comment! I had started out with your Scrubber example, but had managed to greatly overcomplicated things as I added functionality. This comment prompted me to go back and rethink the whole thing, and I’ve now removed the generator function and the whole thing is much simpler and cleaner.

Your help is much appreciated.