Fusion icon indicating copy to clipboard operation
Fusion copied to clipboard

Observer should allow for multiple state objects at once

Open funwolf7 opened this issue 1 year ago • 3 comments

Currently, it is difficult to have a piece of code run whenever one of multiple different state objects change.

Let's say you have two states, State1 and State2, and you want to run an function whenever either of them change.

The most basic way is to use two Observers and run the code whenever any of them change, however this causes some problems.

  • Suppose both states are Computeds that are based on State3. Changing State3 would cause both to change one after the other, making the calculation run twice, the first time with only one of State1 and State2 changed. This can potentially cause issues where the first call doesn't function properly, as it may expect State1 and State2 to be similar in some way. It also leads to ambiguity, as the developer doesn't know which one will change first (and it may even be pseudo-random depending on the way the graph works), and could be problematic if the function is something like an HTTP call where you want to reduce the amount of times it runs.
  • The issues above will be made worse if #150 gets added, as State1 and State2 may be set in the same bulk set/transaction, causing the same problems.
  • This is less important, but it is tedious to have to define a function to use it a couple times right below and then never again. You also have to call multiple functions to disconnect them all.

A current workaround is to create a Computed that calls use on all the state objects and returns something unique every time, then attaching an Observer to it. This gets rid of the problems as Computeds will only update once, after all their dependencies have updated.

Observer(Computed(function(use)
	use(State1)
	use(State2)
	return {}
end)):onChange(function()
	-- do whatever
end)

However this isn't an obvious solution, and has its own tedium of writing a use call for every state and returning a table. It is probably also less efficient than an official method would be.

Thus, I propose a way to have an observer take multiple state objects, and run the function only after all of them that will update actually update. I'm not sure what the best API implementation would be, but I'm sure there are plenty of elegant methods.

funwolf7 avatar Jun 29 '23 21:06 funwolf7

Why not just

Observer({state1, state2, ...}):onChange(...)

From what the computed example has, it would still update every time either one of the two states changes? Unless I'm not understanding lol

krypt102 avatar Jul 03 '23 05:07 krypt102

This is definitely something I've thought about. I would quite like to unify the design of Observers with that of Computeds etc, but the historical problem has been that the previous automatic dependency manager was incompatible with code that could yield (which Observers are explicitly designed to support).

This might not be such a problem now that we have the new use() callbacks. This may be worth thinking about.

dphfox avatar Jul 24 '23 00:07 dphfox

Related to #4 - the design of Eventual also provisions for use callbacks in yielding contexts. The behaviour of these two objects should be aligned with respect to how dependencies are captured after the first yield.

dphfox avatar Aug 15 '23 12:08 dphfox