com.wire.signals
Type members
Classlikes
A signal which initializes its value by executing the loader
future and then updates the value by composition of
the previous value and an event published in the associated source
stream.
You may think of it as a more performance-efficient version of RefreshingSignal
, useful when the loader
requires heavy computations but an update between one value and another is simple in comparison. For example:
A signal which initializes its value by executing the loader
future and then updates the value by composition of
the previous value and an event published in the associated source
stream.
You may think of it as a more performance-efficient version of RefreshingSignal
, useful when the loader
requires heavy computations but an update between one value and another is simple in comparison. For example:
val loader: Future[ Vector[DBEntry] ] = fetchDBTableData()
val sourceStream: EventStream[DBEntry] = newDBTableEntryStream()
val updater: (Vector[DBEntry], DBEntry) => Vector[DBEntry] = { (table, newEntry) => table :+ newEntry }
val signal = new AggregatingSignal(loader, sourceStream, updater)
Here, the loader
fetches the whole DB table, but if we know that the only change to that table is that new entries
can be added to it, we can avoid calling the loader
every time the event comes. Instead, we can create the updater
function which will combine the current value of the signal (i.e. the in-memory cache of the DB table, created by
calling the loader
only once, when the signal was initialized), with the new entry.
- Type Params
- E
The type of the update events.
- V
The type of the value held in the signal and the result of the
loader
execution.
- Value Params
- ec
The execution context in which the
loader
is executed.- loader
A future used for computing the initial value of the signal. It's passed by name, so if it is created in the place of argument, it will be executed for the first time only when the first subscriber function is registered in the signal, or immediately if
disableAutowiring
is used. If a new event comes while theloader
not yet finished, the event will be memorized and used to produce the first updated value right afterwards.- sourceStream
An event stream publishing events which will be used to update the value of the signal.
- updater
A function combining the current value of the signal with a new event to produce the updated value.
- See also
- Companion
- object
Provides the default implementation of the Subscription trait.
Exposes two new abstract methods: onSubscribe
and onUnsubscribe
. A typical way to implement them is
to have a reference to the source of events which implements the EventSource trait and call subscribe(this)
on that source (where this
is the subscription).
Provides the default implementation of the Subscription trait.
Exposes two new abstract methods: onSubscribe
and onUnsubscribe
. A typical way to implement them is
to have a reference to the source of events which implements the EventSource trait and call subscribe(this)
on that source (where this
is the subscription).
For examples:
- Value Params
- context
A weak reference to the event context within which the subscription lives.
- See also
A subclass of CancellableFuture
which represents a subset of cancellable futures which are actually cancellable.
I know it means the name of the parent class is misleading, since not all cancellable futures are cancellable, but,
well, naming is a hard problem in computer science.
The reasoning here goes like this: An actually cancellable CancellableFuture
is a wrapper over a Promise
, not
a Future
. We create a promise and try to fulfill it with the code given to us in one of the constructor methods.
If we succeed, the cancellable future will behave just as a regular one. But thanks to that we hold a promise,
we are able to cancel the ongoing execution of that piece of code by calling tryFailure
on the promise.
A subclass of CancellableFuture
which represents a subset of cancellable futures which are actually cancellable.
I know it means the name of the parent class is misleading, since not all cancellable futures are cancellable, but,
well, naming is a hard problem in computer science.
The reasoning here goes like this: An actually cancellable CancellableFuture
is a wrapper over a Promise
, not
a Future
. We create a promise and try to fulfill it with the code given to us in one of the constructor methods.
If we succeed, the cancellable future will behave just as a regular one. But thanks to that we hold a promise,
we are able to cancel the ongoing execution of that piece of code by calling tryFailure
on the promise.
However, in some situations cancelling will not be possible. One is of course that the future may already be completed -
with success or with failure (including that it might have been already cancelled). This is why the cancel
method
returns a boolean - we can't be always 100% sure that calling it will actually cancel the future.
But there is also another possibility. Because of how ubiquitous futures are in the Scala standard library, it makes
sense to be able to wrap them in cancellable futures so that they can be used in the same code with the actually
cancellable futures. After all, if we're on the happy path, the two behave the same. The only difference appears
only when we try to cancel such a "cancellable" future - we don't have access to the parent promise of that future,
so we can't cancel it. Calling cancel
on such a future will always return false
, a callback registered with
onCancel
will be ignored, etc.
The last case is an inverse of the second one: Sometimes we have an actually cancellable future but we want to
make sure that it won't be cancelled in a given piece of code. Imagine a situation when we wait for a result of
complex computations which are modelled as a cancellable future. The computations may be cancelled by one class
that manages the computations, but we also give a reference to that cancellable future to a few other classes in
the program, so that when the computations finish, those classes will be immediately informed, get the result, and
use it. But they shouldn't be able to cancel the original computations - even if the given class becomes disinterested
in the result, it should not be able to stop the computations for all others.
In such case, we can use the method toUncancellable
- it will give us an Uncancellable
wrapped over
the promise.future
of our original Cancellable
future.
- Type Params
- T
The result type of the cancellable future
- Value Params
- ec
The execution context in which the future will be executed. By default it's the default context set in the
Threading
class- promise
The promise a new cancellable future wraps around
CancellableFuture
is an object that for all practical uses works like a future but enables the user to cancel the operation.
A cancelled future fails with CancellableFuture.CancelException
so the subscriber can differentiate between thisand other
failure reasons.
CancellableFuture
is an object that for all practical uses works like a future but enables the user to cancel the operation.
A cancelled future fails with CancellableFuture.CancelException
so the subscriber can differentiate between thisand other
failure reasons.
- See also
- Companion
- class
CancellableFuture
is an object that for all practical uses works like a future but enables the user to cancel the operation.
A cancelled future fails with CancellableFuture.CancelException
so the subscriber can differentiate between this and other
failure reasons. It is impossible to cancel a future if it is already completed or if it is uncancellable.
CancellableFuture
is an object that for all practical uses works like a future but enables the user to cancel the operation.
A cancelled future fails with CancellableFuture.CancelException
so the subscriber can differentiate between this and other
failure reasons. It is impossible to cancel a future if it is already completed or if it is uncancellable.
- See also
Uncancellable
for details on uncancellable futures- Companion
- object
A signal holding an immutable value. Using const signals in flatMap chains should have better performance compared to source signals with the same value. Since the value never changes, the subscriber function will be called only in the moment of subscription, but never after that, so there's no need to keep the subscription.
A signal holding an immutable value. Using const signals in flatMap chains should have better performance compared to source signals with the same value. Since the value never changes, the subscriber function will be called only in the moment of subscription, but never after that, so there's no need to keep the subscription.
- Companion
- object
A thin wrapper over Scala's ExecutionContext
allowing us to differentiate between the default execution context
which tries to run asynchronously as many tasks as possible, and limited execution contexts, allowed to run only up
to a given number of tasks at once.
A thin wrapper over Scala's ExecutionContext
allowing us to differentiate between the default execution context
which tries to run asynchronously as many tasks as possible, and limited execution contexts, allowed to run only up
to a given number of tasks at once.
- See also
ExecutionContext
- Companion
- object
When you subscribe to an EventSource in return you receive a Subscription. You can use that subscription to unsubscribe from the event source or to temporarily pause receiving events. But managing a big number of subscriptions to different event sources can be tricky. EventContext comes to the rescue.
When you subscribe to an EventSource in return you receive a Subscription. You can use that subscription to unsubscribe from the event source or to temporarily pause receiving events. But managing a big number of subscriptions to different event sources can be tricky. EventContext comes to the rescue.
By default, every subscription is registered in a "dummy" EventContext.Global which lives for the lifetime of the whole program and does nothing. But if instead you will create a new EventContext and use it explicitly when subscribing or you will set it as an implicit parameter, taking over EventContext.Global, the subscription will be registered within this new one. It will allow you to manage all registered subscriptions at once and all registered subscriptions will be destroyed when the event context lifetime ends.
Usage of methods in the trait are explained as they are implemented in the default implementation. All operations on an EventContext are synchronized.
- See also
- Companion
- object
An event stream of type E
dispatches events (of type E
) to all functions of type (E) => Unit
which were registered in
the event stream as its subscribers. It doesn't have an internal state. It provides a handful of methods which enable
the user to create new event streams by means of composing the old ones, filtering them, etc., in a way similar to how
the user can operate on standard collections, as well as to interact with Scala futures, cancellable futures, and signals.
Please note that by default an event stream is not able to receive events from the outside - that functionality belongs
to SourceStream.
An event stream of type E
dispatches events (of type E
) to all functions of type (E) => Unit
which were registered in
the event stream as its subscribers. It doesn't have an internal state. It provides a handful of methods which enable
the user to create new event streams by means of composing the old ones, filtering them, etc., in a way similar to how
the user can operate on standard collections, as well as to interact with Scala futures, cancellable futures, and signals.
Please note that by default an event stream is not able to receive events from the outside - that functionality belongs
to SourceStream.
An event stream may also help in sending events from one execution context to another. For example, a source stream may receive an event in one execution context, but the function which consumes it is registered with another execution context specified. In that case the function won't be called immediately, but in a future executed in that execution context.
- See also
ExecutionContext
- Companion
- object
An event stream coupled with an auxiliary signal. You can use it if you want to repeat some computations based on the current value of the signal every time when an event is published in the source stream.
An event stream coupled with an auxiliary signal. You can use it if you want to repeat some computations based on the current value of the signal every time when an event is published in the source stream.
val aux = Signal[Int]()
val source = EventStream[Unit]()
val newStream = EventStreamWithAuxSignal(source, aux)
newStream.foreach { case (_, Option(n)) => /* ... */ }
Here, newStream
extends EventStream[Unit, Option[Int]]
.
The subscriber function registered in newStream`` will be called every time a new unit event is published in
sourceand it will receive a tuple of the event and the current value of
aux:
Some[Int]if something was already published in the signal, or
None` if it is not initialized yet.
- Type Params
- A
The type of events in the source stream.
- B
The type of values in the auxiliary signal.
- Value Params
- aux
An auxiliary signal of values of the type
B
. Every time a new event is published insource
, this stream will access the signal for its current value. The value (or lack of it) will become the second part of the tuple published in this stream.- source
The source event stream used to trigger events in this event stream. Every event of type
A
published insource
will become the first part of the tuple published in this stream.
- Companion
- object
A dispatch queue limiting number of concurrently executing tasks.
All tasks are executed on parent execution context, but only up to the concurrencyLimit
.
New tasks, scheduled when the limit is reached, will wait in the queue until one of the current one finishes.
Create with one of DispatchQueue.apply
methods.
A dispatch queue limiting number of concurrently executing tasks.
All tasks are executed on parent execution context, but only up to the concurrencyLimit
.
New tasks, scheduled when the limit is reached, will wait in the queue until one of the current one finishes.
Create with one of DispatchQueue.apply
methods.
- Companion
- object
By default, a new signal is initialized lazily, i.e. only when the first subscriber function is registered in it.
You can decorate it with NoAutowiring
to enforce initialization.
By default, a new signal is initialized lazily, i.e. only when the first subscriber function is registered in it.
You can decorate it with NoAutowiring
to enforce initialization.
- See also
A superclass for all event streams which compose other event streams into one.
A superclass for all event streams which compose other event streams into one.
- Type Params
- A
The type of the events emitted by all the source streams.
- E
The type of the events emitted by the stream constructed from the sources.
- Value Params
- sources
A variable arguments list of event streams serving as sources of events for the resulting stream.
A signal which initializes its value by executing the loader
cancellable future and then updates the value the same way
every time a new refresh event is published in the associated event stream. The type of the event is not important.
A signal which initializes its value by executing the loader
cancellable future and then updates the value the same way
every time a new refresh event is published in the associated event stream. The type of the event is not important.
A typical use case for a refreshing signal might be, for example, to inform another component that something changed in the storage while already retrieving the updated data. In this case, the refresh event stream can be anything that indicates the data has changed, and the loader is the query. The refresh event might even be a false positive: then the loader function will be called but the subscriber function of the refreshing signal will not be notified as the result of the loader is the same and so the value of the signal doesn't change.
- Type Params
- V
The value type of the signal and the result of the
loader
cancellable future.
- Value Params
- ec
The execution context in which the
loader
is executed.- loader
A cancellable future computing the value of the signal. It's passed by name, so if it is created in the place of argument, it will be executed for the first time only when the first subscriber function is registered in the signal, or immediately if
disableAutowiring
is used. If the execution fails or is cancelled, the value of the signal won't be updated.- refreshStream
An event stream publishing events which will trigger new executions of the
loader
. If a new event comes before the previous call toloader
finishes, the previous call will be cancelled.
- See also
- Companion
- object
A special case of a limited dispatch queue which allows for only one task to be executed at once. Use when you want to enforce the tasks to be executed in the order they were scheduled.
A special case of a limited dispatch queue which allows for only one task to be executed at once. Use when you want to enforce the tasks to be executed in the order they were scheduled.
- Companion
- object
A utility object for serializing futures.
A utility object for serializing futures.
The need for this functionality comes from the fact that we can't assume an event will be processed before the next one comes,
but sometimes it is also crucial to process the next event only after the first one is done. In such case, the user can use
one of the methods of Serialized
to schedule processing the first event, tag it with a key, and then simply use the same key
to schedule processing of the second event. The user doesn't have to know if the first event was already processed or not -
if yes, processing of the second will start immediately, if not, the processing (in the form of a future or a CancellableFuture)
will be attached to the end of the ongoing processing and triggered only after it's done.
- Todo
Serialized
is currently used in only one place in wire-signals,FutureEventStream
, which in turn is used only for EventStream.mapSync, so we may think or removing this class from the library (move to extensions, maybe?).
A signal is an event stream with a cache.
A signal is an event stream with a cache.
Whereas an event stream holds no internal state and just passes on events it receives, a signal keeps the last value it received. A new subscriber function registered in an event stream will be called only when a new event is published. A new subscriber function registered in a signal will be called immediately (or as soon as possible on the given execution context) with the current value of the signal (unless it's not initialized yet) and then again when the value changes. A signal is also able to compare a new value published in it with the old one - the new value will be passed on only if it is different. Thus, a signal can help us with optimizing performance on both ends: as a cache for values which otherwise would require expensive computations to produce them every time we need them, and as a way to ensure that subscriber functions are called only when the value actually changes, but not when the result of the intermediate computation is the same as before.
Note that for clarity we talk about events in the event streams, but about values in signals.
An signal of the type V
dispatches values to all functions of the type (V) => Unit
which were registered in
the signal as its subscribers. It provides a handful of methods which enable the user to create new signals by means of composing
the old ones, filtering them, etc., in a way similar to how the user can operate on standard collections, as well as to interact with
Scala futures, cancellable futures, and event streams. Please note that by default a signal is not able to receive events from the outside -
that functionality belongs to SourceSignal.
- Type Params
- V
The type of the value held in the signal.
- Value Params
- value
The option of the last value published in the signal or
None
if the signal was not initialized yet.
- See also
- Companion
- object
The usual entry point for publishing values in signals.
The usual entry point for publishing values in signals.
Create a new signal either using the default constructor or the Signal.apply[V]()
method. The source signal exposes
methods you can use for changing its value. Then you can combine it with other signals and finally subscribe a function
to it which will be called initially, and then on each change of the signal's value.
- Type Params
- V
the type of the value held by the signal.
- Companion
- object
The usual entry point for publishing events.
The usual entry point for publishing events.
Create a new source stream either using the default constructor or the EventStream.apply[V]()
method. The source stream exposes
methods you can use for publishing new events. Then you can combine it with other event streams and finally subscribe a function
to it which will receive the resulting events.
- Type Params
- E
the type of the event
When you add a new subscriber to your EventStream or Signal, in return you get a Subscription. A subscription can then be used to inform the source about changes in the condition of the connection: should it be enabled or disabled, should the subscriber be subscribed or (temporarily) unsubscribed, or should the subscription be permanently destroyed.
When you add a new subscriber to your EventStream or Signal, in return you get a Subscription. A subscription can then be used to inform the source about changes in the condition of the connection: should it be enabled or disabled, should the subscriber be subscribed or (temporarily) unsubscribed, or should the subscription be permanently destroyed.
It is important to destroy subscriptions when they are no longer needed, e.g. at the end of the life of an object which subscribes to the source of events. Otherwise you may face a hidden memory leak where no longer used data cannot be GC-ed because it is still referenced by the source of events.
- See also
EventContext Implement this trait together with writing a new event source if you want to change how your event source reacts to the aforementioned events. For an example of how to do it on a small scale, please
Use Threading
to set up the default execution context which will be later used as the parent for other
dispatch queues and to run cancellable futures, event streams, and signals, if no other execution context
is provided.
Use Threading
to set up the default execution context which will be later used as the parent for other
dispatch queues and to run cancellable futures, event streams, and signals, if no other execution context
is provided.
A signal which publishes changes of its parent signal but no more often than once during a given time interval. The initial value of the parent signal will be published immediately. The first change to it will happen at the earliest after the given delay. If the parent signal changes its value more often, the intermediate values will be ignored.
A signal which publishes changes of its parent signal but no more often than once during a given time interval. The initial value of the parent signal will be published immediately. The first change to it will happen at the earliest after the given delay. If the parent signal changes its value more often, the intermediate values will be ignored.
Use it e.g. for optimization of a signal chain when there is no need to react immediately to all changes to the original signal. For example. changes in the UI could be displayed only with the speed that allows for comfortable usage of the app by the user, but not faster.
- Type Params
- V
The value type of the signal.
- Value Params
- delay
The time interval used for publishing. No more than one change of the value per
delay
will be published.- source
The original signal providing the value and changes to it.
- Todo
Check if when the original value changes once during the delay interval, but not again after it, will that one change be noticed. I think it should be.
- Companion
- object
A subclass of CancellableFuture
which represents a subset of cancellable futures which are actually uncancellable.
I know it means the name of the parent class is misleading, since not all cancellable futures are cancellable, but,
well, naming is a hard problem in computer science.
The reasoning here goes like this: An actually cancellable CancellableFuture
is a wrapper over a Promise
, not
a Future
. We create a promise and try to fulfill it with the code given to us in one of the constructor methods.
If we succeed, the cancellable future will behave just as a regular one. But thanks to that we hold a promise,
we are able to cancel the ongoing execution of that piece of code by calling tryFailure
on the promise.
A subclass of CancellableFuture
which represents a subset of cancellable futures which are actually uncancellable.
I know it means the name of the parent class is misleading, since not all cancellable futures are cancellable, but,
well, naming is a hard problem in computer science.
The reasoning here goes like this: An actually cancellable CancellableFuture
is a wrapper over a Promise
, not
a Future
. We create a promise and try to fulfill it with the code given to us in one of the constructor methods.
If we succeed, the cancellable future will behave just as a regular one. But thanks to that we hold a promise,
we are able to cancel the ongoing execution of that piece of code by calling tryFailure
on the promise.
However, in some situations cancelling will not be possible. One is of course that the future may already be completed -
with success or with failure (including that it might have been already cancelled). This is why the cancel
method
returns a boolean - we can't be always 100% sure that calling it will actually cancel the future.
But there is also another possibility. Because of how ubiquitous futures are in the Scala standard library, it makes
sense to be able to wrap them in cancellable futures so that they can be used in the same code with the actually
cancellable futures. After all, if we're on the happy path, the two behave the same. The only difference appears
only when we try to cancel such a "cancellable" future - we don't have access to the parent promise of that future,
so we can't cancel it. Calling cancel
on such a future will always return false
, a callback registered with
onCancel
will be ignored, etc.
The last case is an inverse of the second one: Sometimes we have an actually cancellable future but we want to
make sure that it won't be cancelled in a given piece of code. Imagine a situation when we wait for a result of
complex computations which are modelled as a cancellable future. The computations may be cancelled by one class
that manages the computations, but we also give a reference to that cancellable future to a few other classes in
the program, so that when the computations finish, those classes will be immediately informed, get the result, and
use it. But they shouldn't be able to cancel the original computations - even if the given class becomes disinterested
in the result, it should not be able to stop the computations for all others.
In such case, we can use the method toUncancellable
- it will give us an Uncancellable
wrapped over
the promise.future
of our original Cancellable
future.
- Type Params
- T
The result type of the cancellable future
- Value Params
- ec
The execution context in which the future will be executed. By default it's the default context set in the
Threading
class- future
The future a new uncancellable future wraps around
A dispatch queue that simply passes all its tasks to its execution context.
A dispatch queue that simply passes all its tasks to its execution context.
- Companion
- object