Reactive Programming
Reactive Programming is a declarative programming paradigm concerned with data streams and the propagation of change. With this paradigm, it is possible to express static (e.g., arrays) or dynamic (e.g., event emitters) data streams with ease.
1. The Core Concept
In imperative programming, a = b + c means a is assigned the result of b + c at that instant. If b changes later, a remains unchanged.
In reactive programming, a automatically updates whenever b or c changes, without explicit re-assignment. Think of a spreadsheet: if you update cell B1, cell A1 (which depends on B1) updates automatically.
2. Streams and Observables
Everything is a stream. Variables, user inputs, properties, caches, data structures, etc., can all be viewed as streams of events over time.
- Observable: A source of data stream. It emits items over time.
- Observer: A consumer that subscribes to an Observable to react to new data.
- Subscription: The connection between an Observable and an Observer.
3. Operators
Reactive libraries provide powerful operators to manipulate streams. These are often functional in nature.
- Creation:
from,of,interval. - Transformation:
map,flatMap,buffer. - Filtering:
filter,debounce,throttle. - Combination:
merge,combineLatest,zip.
Example (RxJS)
import { fromEvent } from 'rxjs';
import { throttleTime, map, scan } from 'rxjs/operators';
// Create a stream from button clicks
fromEvent(document.querySelector('button'), 'click')
.pipe(
throttleTime(1000), // Allow at most 1 click per second
map(event => event.clientX), // Map event to X coordinate
scan((count, clientX) => count + clientX, 0) // Accumulate values
)
.subscribe(count => console.log(`Clicked! Total: ${count}`));
4. Backpressure
Backpressure is a critical concept in reactive systems. It deals with the situation where the data source (Producer) is emitting data faster than the consumer (Subscriber) can process it.
- Buffering: Store excess items in memory (risk of OutOfMemory).
- Dropping: Discard items (sampling).
- Control: The consumer signals the producer to slow down (Reactive Streams specification).
5. Common Libraries
- RxJS: Reactive Extensions for JavaScript.
- RxJava: Reactive Extensions for Java.
- Project Reactor: Foundation for Spring WebFlux.
6. Hot vs Cold Observables
Understanding the difference between Hot and Cold Observables is crucial for managing side effects.
Cold Observables
- Unicast: Each subscriber gets its own independent execution of the observable.
- Lazy: The producer is created inside the observable and starts only when a subscription happens.
- Example: An HTTP request. If two people subscribe, two separate network requests are made.
- Analogy: Watching a movie on Netflix. You start it when you want, and you see it from the beginning.
Hot Observables
- Multicast: All subscribers share the same execution.
- Active: The producer is created outside the observable and may be emitting items even before anyone subscribes.
- Example: Mouse clicks, WebSockets, Stock Tickers. If you subscribe late, you miss the earlier events.
- Analogy: Watching a live TV broadcast. You join the stream in progress.
7. Schedulers (Threading)
Reactive libraries allow you to control concurrency and threading easily using Schedulers. You can switch between threads for different parts of the processing pipeline.
subscribeOn: Specifies which thread the Observable should start working on (e.g., making a network call on a background thread).observeOn: Specifies which thread the downstream operators and the final subscriber should run on (e.g., updating the UI on the Main thread).
Common Scheduler Types
- IO: For I/O-bound tasks (network requests, database reads). Backed by a cached thread pool.
- Computation: For CPU-bound tasks (event loops, processing large data). Backed by a fixed thread pool sized to the number of CPU cores.
- Main/UI: For interacting with the user interface (Android Main Thread, JavaFX thread).
programming/asynchronous-programming programming/functional-programming programming/throttling-pattern