Observables
Observables are like Ref
s but you can listen to changes.
julia> using Interact
julia> observable = Observable(0)
Observable{Int64} with 0 listeners. Value:
0
julia> h = on(observable) do val
println("Got an update: ", val)
end
#1 (generic function with 1 method)
julia> observable[] = 42
Got an update: 42
42
To get the value of an observable index it with no arguments
julia> observable[]
42
To remove a handler use off
with the return value of on
:
julia> off(observable, h)
How is it different from Reactive.jl?
The main difference is Signal
s are manipulated mostly by converting one signal to another. For example, with signals, you can construct a changing UI by creating a Signal
of UI objects and rendering them as the signal changes. On the other hand, you can use an Observable both as an input and an output. You can arbitrarily attach outputs to inputs allowing structuring code in a signals-and-slots kind of pattern.
Another difference is Observables are synchronous, Signals are asynchronous. Observables may be better suited for an imperative style of programming.
API
Type
Observables.Observable
— Type.Like a Ref
but updates can be watched by adding a handler using on
.
Functions
Observables.on
— Method.on(f, o::AbstractObservable)
Adds function f
as listener to o
. Whenever o
's value is set via o[] = val
f
is called with val
.
Observables.off
— Method.off(o::AbstractObservable, f)
Removes f
from listeners of o
.
Base.setindex!
— Method.o[] = val
Updates the value of an Observable
to val
and call its listeners.
Base.getindex
— Method.o[]
Returns the current value of o
.
Observables.onany
— Method.onany(f, args...)
Calls f
on updates to any oservable refs in args
. args
may contain any number of Observable
ojects. f
will be passed the values contained in the refs as the respective argument. All other ojects in args
are passed as-is.
Base.map!
— Method.map!(f, o::Observable, args...)
Updates o
with the result of calling f
with values extracted from args. args
may contain any number of Observable
ojects. f
will be passed the values contained in the refs as the respective argument. All other ojects in args
are passed as-is.
Observables.connect!
— Method.connect!(o1::Observable, o2::Observable)
Forward all updates to o1
to o2
Base.map
— Method.map(f, o::Observable, args...)
Creates a new oservable ref which contains the result of f
applied to values extracted from args. The second argument o
must be an oservable ref for dispatch reasons. args
may contain any number of Observable
ojects. f
will be passed the values contained in the refs as the respective argument. All other ojects in args
are passed as-is.
Observables.throttle
— Method.throttle(dt, input::AbstractObservable)
Throttle a signal to update at most once every dt
seconds. The throttled signal holds the last update of the input
signal during each dt
second time window.
Macros
Observables.@map
— Macro.@map(expr)
Wrap AbstractObservables
in &
to compute expression expr
using their value. The expression will be computed when @map
is called and every time the AbstractObservables
are updated.
Examples
julia> a = Observable(2);
julia> b = Observable(3);
julia> c = Observables.@map &a + &b;
julia> c[]
5
julia> a[] = 100
100
julia> c[]
103
Observables.@map!
— Macro.@map!(d, expr)
Wrap AbstractObservables
in &
to compute expression expr
using their value: the expression will be computed every time the AbstractObservables
are updated and d
will be set to match that value.
Examples
julia> a = Observable(2);
julia> b = Observable(3);
julia> c = Observable(10);
julia> Observables.@map! c &a + &b;
julia> c[]
10
julia> a[] = 100
100
julia> c[]
103
Observables.@on
— Macro.@on(expr)
Wrap AbstractObservables
in &
to execute expression expr
using their value. The expression will be computed every time the AbstractObservables
are updated.
Examples
julia> a = Observable(2);
julia> b = Observable(3);
julia> Observables.@on println("The sum of a+b is $(&a + &b)");
julia> a[] = 100;
The sum of a+b is 103