From 39453c20cba870c331be05eb68045ebcee24853b Mon Sep 17 00:00:00 2001 From: Yakir Gagnon <12.yakir@gmail.com> Date: Fri, 6 May 2022 11:28:25 +0200 Subject: [PATCH 1/2] add onall --- Project.toml | 2 +- src/Observables.jl | 34 +++++++++++++++++++++++++++++++++- test/runtests.jl | 19 ++++++++++++++++--- 3 files changed, 50 insertions(+), 5 deletions(-) diff --git a/Project.toml b/Project.toml index d410640..ba0890f 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "Observables" uuid = "510215fc-4207-5dde-b226-833fc4488ee2" -version = "0.5.1" +version = "0.5.2" [compat] julia = "1.6" diff --git a/src/Observables.jl b/src/Observables.jl index 31da44d..90e8459 100644 --- a/src/Observables.jl +++ b/src/Observables.jl @@ -1,6 +1,6 @@ module Observables -export Observable, on, off, onany, connect!, obsid, async_latest, throttle +export Observable, on, off, onany, onall, connect!, obsid, async_latest, throttle export Consume, ObserverFunction, AbstractObservable import Base.Iterators.filter @@ -348,6 +348,38 @@ function onany(f, args...; weak::Bool = false, priority::Int=0) return obsfuncs end + +onall(f) = error("onall needs at least two observables") +onall(f, obs1) = error("onall needs at least two observables") + +""" + onall(f, args...) + +Calls `f` on updates to all observable refs in `args`. +`f` is called only if all (i.e. not any) observable refs in `args` are updated at least once. +`args` may contain any number of `Observable` objects. +`f` will be passed the values contained in the refs as the respective argument. +All other objects in `args` are passed as-is. + +See also: [`on`](@ref). +""" +function onall(f, observables...; condition=(old, new)-> true) + updated = fill(false, length(observables)) + for (i, observable) in enumerate(observables) + old = observable[] + on(observable) do new_value + if condition(old, new_value) + updated[i] = true + if all(updated) + f(to_value.(observables)...) + fill!(updated, false) + end + end + old = new_value + end + end +end + """ map!(f, observable::AbstractObservable, args...; update::Bool=true) diff --git a/test/runtests.jl b/test/runtests.jl index 6c7bd52..0901c7b 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -167,7 +167,7 @@ end r[] = 3 # shouldn't call test end -@testset "onany and map" begin +@testset "onany, onall, and map" begin r = Observable(0) tested = Ref(false) onany(1, r) do x, y @@ -178,6 +178,19 @@ end r[] = 2 @test tested[] + r1 = Observable(0) + r2 = Observable(0) + tested = Ref(false) + onall(r1, r2) do x, y + @test x == 1 + @test y == 2 + tested[] = true + end + r1[] = 1 + @test !tested[] + r2[] = 2 + @test tested[] + r1 = Observable(0) r2 = Observable(0) map!(x->x+1, r2, r1) @@ -339,14 +352,14 @@ end o = Observable(0) cnt[] = 0 - function compute_something(x) + function compute_something_else(x) for i=1:10^8; rand() end cnt[] = cnt[] + 1 end sleep(1) o_latest = async_latest(o, 3) - on(compute_something, o_latest) # compute something on the latest update + on(compute_something_else, o_latest) # compute something on the latest update for i=1:5 o[] = i end From 6495575d8b64ab57d5fe23049456d09dce2fbd0a Mon Sep 17 00:00:00 2001 From: Yakir Luc Gagnon <12.yakir@gmail.com> Date: Sun, 8 May 2022 09:06:54 +0200 Subject: [PATCH 2/2] Update src/Observables.jl Co-authored-by: Anshul Singhvi --- src/Observables.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Observables.jl b/src/Observables.jl index 90e8459..1c0cfda 100644 --- a/src/Observables.jl +++ b/src/Observables.jl @@ -356,7 +356,7 @@ onall(f, obs1) = error("onall needs at least two observables") onall(f, args...) Calls `f` on updates to all observable refs in `args`. -`f` is called only if all (i.e. not any) observable refs in `args` are updated at least once. +`f` is called only if *all* (as opposed to any) observable refs in `args` are updated at least once. `args` may contain any number of `Observable` objects. `f` will be passed the values contained in the refs as the respective argument. All other objects in `args` are passed as-is.