JuliaGizmos / Observables.jl

observable refs
Other
105 stars 32 forks source link

Synchronized updates #109

Open ffreyer opened 1 year ago

ffreyer commented 1 year ago

This is an attempt at a more formal solution to problems such as

input1 = Observable([1, 2])
input2 = Observable([1, 2])
output = Observable([(1, 1)])
f = onany(input1, input2) do a, b
    output[] = tuple.(a, b)
    return  
end

input1[] = [1, 2, 3]
input2[] = [1, 2, 3]

triggering a DimensionMismatch error due to input1 executing first. The current solution to this is to write to the Observable directly with input1.val = [1, 2, 3] first, and then trigger an update with the second observable as usual.

While that works it requires you to modify a field of the observable which isn't great in my opinion. In a more complex network of observables you may also need to trigger both observables to run everything you need, which will double up on updates attached to both.

This pr is adding a different way to handle this:

@combine_update begin
    input1[] = [1, 2, 3, 4]
    input2[] = [1, 2, 3, 4]
end

which lowers to

begin
    prepare_update!(input1, [1, 2, 3, 4])
    prepare_update!(input2, [1, 2, 3, 4])
    execute_update!(input1)
    execute_update!(input2)
end

This will update observable.val and mark listeners as out-of-date via prepare_update! and then update them through execute_update!. The latter will skip over listeners that have already been executed and exit when it hits a consuming listener (consuming now or before). So it is effectively the same as

input1.val = [1, 2, 3, 4]
input2.val = [1, 2, 3, 4]
notify(input1)
notify(input2)

except that it skips duplicate execution of listeners.

TODO:

Datseris commented 5 months ago

bump here, if this is a solution, perhaps it can go in?