Understanding push and pull signal in reactive paradigm
Issue #28
The idea of Signal may originate from Elm Reactivity, and it has now been widely adopted in iOS
I once asked What are examples of hot and cold signal in ReactiveCocoa?
- When to use IEnumerable vs IObservable?
- [ReactiveCocoa Framework Overview](Framework Overview)
- Rx – for beginners (part 9): Hot Vs. Cold observable
- RxSwift Hot and Cold Observables
Whether it is hot vs cold, Signal vs Signal Producer, Observable vs Enumerable, … it’s good to understand how it gets implemented, so that to have a good sense of how they work
Monad
Basically, Signal and its Result are just monads, which are thing that can be mapped and chained.
Signal makes use of deferred execution callback blocks, and push vs pull
is just how the Signal updates its value and the order the callbacks are called
Execution callback block is that we pass a function to another function, and it will get called when appropriated
Sync vs Async
Monad can be in either sync or async mode. Sync is easier to understand, but async is somewhat you’re already familiar and used in practice
Basically,
- Sync: you get the returned value right away via
return
- Aync: you get the returned value via callback block
Here is an example of a simple function
1 | // Sync |
Here is an example of Event
1 | // Sync |
Push Signal
Take a look at my Push Signal, called Signal, it is like how Promise A+ Then works
Implementation
1 | public final class Signal<T> { |
Usage
1 | let signal = Signal<String>() |
Callbacks
Given a chained signals like this
A -(map)-> B -(flatMap)-> C -(flatMap)-> D -(subscribe)
- The idea is we send event to the source signal, and it propagates events through via callbacks.
- Triggered by sending event to the source signal.
- We must keep A as it keeps the others around
- We subscribe the last D
- We send event to the first A
- A ‘s callback gets called, it it in turn calls callback of B with the result of A ‘s map, then B ‘s callback calls C ‘s callback with the result of B
‘s flatMap, …
Pull Signal
Take a look at my Pull Signal, called Future
Implementation
Here operation
is a task, when called and completed, will notify its completion
1 | public struct Future<T> { |
Usage
1 | let _ = Future<String> { completion in |
Callbacks
Given a chained signals like this
A -(map)-> B -(flatMap)-> C -(flatMap)-> D -(subscribe)
- The idea is we subscribe to the final signal D, and it cause the previous signals to action.
- Triggered by subscribing to the final signal.
- We must keep D as it keeps the others around
- We subscribe the last D
- D ‘s operation actions, and it cause C ‘s operation to action, … then A ‘s operation actions. It is in A that the task is performed (like fetching network, retrieving database, file access, heavy computation, …) to get the result, and A ‘s completion gets called. Then A’s completion calls B ‘s completion with the result mapped by B ‘s map, … all the way to the subscriber ‘s completion block