ANGULAR RXJS
PART I – THE PROBLEM WITH CALLBACK, EVENTS, & PROMISES
1. What is the scalability in the context of a web application's user interface,
and why is it unacceptable for modern users?
In a web frontend, Scalability is the app's ability to handle large amounts of
data without slowing down.
The problem of scalability happens when an app tries to process too much
data all at once.
Because browsers use a single thread, this overloads the system and causes
could be:
o Frozen UI: Buttons stop responding.
o Lag: Animations stutter.
o Crashes: The browser may force-terminate the page.
Modern users expect instant responsiveness, making this lag completely
unacceptable.
2. How does asynchronous programming solve the UI freeze, and what new
challenges does it introduce?
The Problem: The Single-Thread Lock
o Today modern web users expect real-time, instant response.
o If an application tries to load and process all its data in one massive
block, it locks the browser's single thread, causing the app to freeze.
The Solution: Processing Over Time
o To overcome this issue asynchronous architecture comes for rescue.
o Instead of demanding a mountain of data all at once, the frontend
requests it in the background and handles it piece-by-piece over time
o Imagine a restaurant with only one Waiter (the browser's single
thread) and a Chef in the kitchen (the remote server).
o How Async Solves the UI Freeze:
The Bad Way (Synchronous): The Waiter takes your order,
walks to the kitchen, and stands there waiting until the Chef
finishes cooking. During this time, no other customers can
order, get water, or pay. The restaurant completely freezes!
Waiter => Take Order => to Kitchen => drop order to chef => Wait
The Async Way: The Waiter drops the order off with the Chef
and immediately returns to the dining room to help other
tables (buttons still click, animations still run). When the Chef
pings "order ready!", the Waiter picks it up and serves it. The
single thread is never blocked.
Waiter => Take Order => to Kitchen => drop order to chef => next order
o This can be achieved by building frontend architectures that can
handles data asynchronously over time rather than all at once.
The New Challenges It Introduces:
o Latency (Unpredictable Timing):
Because data travels over a network, you cannot predict
exactly when or in what order it will arrive.
You never know if the Chef will take 2 seconds or 20 seconds to
finish a dish.
o Coordination (Race Conditions):
Managing multiple independent network tasks simultaneously
makes the code highly complex. It easily leads to race
conditions (where an older network response accidentally
overwrites a newer one).
What if Table 2's meal finishes cooking before Table 1's? What
if a customer cancels their order while it's already being
cooked?
o Complexity:
You cannot hold massive datasets in the browser's RAM all at
once; you must build mechanisms to fetch, stream, and clean
up data safely.
Programming the Waiter to juggle dozens of random,
unpredictable "order ready" pings without mixing up tables or
dropping dishes is incredibly difficult with basic code.
3. In simple terms, what is latency, and why does it make asynchronous
coding so difficult to write?
Latency is simply wait time. It is the critical delay between the moment you
send a message (like a database query or a network request) and the
moment you receive a response.
Why it makes coding difficult:
o The "Luxurious" Way (Synchronous): Our brains naturally prefer linear
logic: "Do this, then immediately do that." The code executes in the
exact, predictable order it is written.
o The Reality (Asynchronous): Networked computing introduces
latency. Your code turns into: "Do this, wait for an unpredictable
period of time, and then do that."
Why we must embrace it:
o Leaving an application sitting completely idle while waiting for a slow
network or database response is unacceptable.
o We use asynchronous programming to exploit this latency—allowing
the app to handle user inputs, run animations, and update the UI
during that exact "wait time" window so the user is never blocked.
4. What is synchronous code, and why is it dangerous for web browsers?
Synchronous execution means code runs strictly in order—each line must
wait for the previous one to finish before starting.
Because JavaScript is single-threaded, writing code this way causes severe
issues:
o The UI Freeze: A slow network call or database query forces the
entire app to sit completely idle, creating artificially long load times
and a terrible user experience.
o Browser Crashes: If a script blocks the thread for too long, the
browser flags the page as dead and force-terminates it.
o Event Pile-ups: Rapid user actions (like mouse movements) generate
hundreds of tiny events. Processing these one-by-one synchronously
creates a massive backlog that makes the UI stutter.
o E.g.
The Fix:
o Use non-blocking asynchronous execution (like callbacks or RxJS
streams). This lets the app trigger a task and immediately continue
handling user inputs while the data loads quietly in the background.
5. What is a callback in JavaScript?
A callback is a function passed as an argument into another function, with
the expectation that it will be executed (or "called back") at a later time.
In a single-threaded language like JavaScript, callbacks are the fundamental
building blocks of asynchronous, non-blocking programming.
Example –
Analogy –
o Imagine you are ordering a custom-built bicycle from a local shop:
The Synchronous Way (No Callback): You walk into the shop,
order the bike, and stand at the counter waiting for hours while
the mechanic builds it. You can't leave, look at your phone, or
eat. You are completely blocked.
The Asynchronous Way (With a Callback): You walk in, order
the bike, and give the mechanic your phone number (the
callback). You leave to go grab lunch and run errands. Once the
bike is ready, the mechanic dials your number to let you know.
6. What is "Callback Hell," and how do RxJS Observables solve it?
While callbacks work great for a single asynchronous task, they quickly
break down when you need to run multiple asynchronous tasks in a specific
order.
Nesting callbacks inside other callbacks creates a deeply indented,
unreadable triangle of code known as "Callback Hell" (or the "Pyramid of
Doom").
The Problem: Callback Hell (Traditional JS)
o Imagine you need to:
Fetch a User profile.
Use the user's ID to fetch their Orders.
Use the latest order ID to fetch the Shipping Status.
o E.g. -
7. While callbacks prevent the application from freezing, what structural or
architectural problems might arise if an application relies too heavily on
them for complex workflows?
The Single-Threaded Problem:
o JavaScript can only execute one line of code at a time (single-
threaded). If it performs a slow task synchronously—like waiting 5
seconds for a server to respond—the entire browser freezes, and the
user cannot click buttons or scroll.
Non-blocking code with callback functions
o Callback functions were created to tackle the problem of blocking for
long-running operations to complete by allowing you to provide a
handler function that the JavaScript runtime will invoke once the
data is ready for use. In the meantime, your application can continue
carrying out any other task.
o To maintain usability, JavaScript uses an asynchronous design:
The Hand-Off: When you make an HTTP request, you provide a
handler function (the callback).
The Background Task: The runtime environment processes the
request in the background.
The Continuation: Instead of waiting, the main program
immediately jumps to the next lines of code (e.g., rendering
the UI).
The Callback: Once the data arrives, the runtime system "calls
you back" by executing your handler function.
8. What is the main downside of asynchronous code compared to
synchronous code regarding "Application State"?
OR
What is the main difference between reasoning about synchronous state
versus asynchronous state?
Synchronous State: State is an information stored in a variable. It is just a
snapshot of your variables at a single point in time. Because code executes
in a straight line ("Do step 1, then step 2"), it is easy to look at any line of
code and know exactly what the variables are and what happens next.
Asynchronous State: Asynchronous code forces you to reason about the
application's future state. Because background tasks complete at
completely unpredictable times, you can no longer guarantee the exact
order in which your code will finish.
9. Why do asynchronous tasks become dangerous when they share "Global
State" or cause "Side Effects"?
A side effect occurs when a function reads or modifies an external resource
outside of itself—like updating a global variable, modifying the DOM, or
writing to a database.
If independent asynchronous functions share global state or cause side
effects, their behavior becomes completely unreliable. Because you cannot
predict which background task will finish first, they will constantly overwrite
or disrupt each other depending on the random order in which they
terminate.
(Reactive programming fixes this by using pure functions, which avoid side effects
and make asynchronous code highly predictable)
[Link] is a "Temporal Dependency" (Time Coupling), and how do
traditional callbacks solve it?
A temporal dependency means that Step 2 cannot begin until Step 1 has
finished because Step 2 relies on the data produced by Step 1. They are
chained together in time.
To guarantee that independent background tasks execute in the exact
correct order, developers traditional use composed (nested) callbacks.
The RxJS Pivot: This is the exact problem RxJS is designed to elegantly
crush. Instead of nesting callbacks to manage temporal dependencies, RxJS
treats these time-coupled steps as a clean, linear stream where data flows
naturally from one operator to the next.
[Link] callbacks out of the picture? Are callbacks completely dead? When
should we actually use RxJS?
No, callbacks are not out of the picture.
Callbacks are the perfect solution for simple tasks. If you are writing a basic
script that makes a single HTTP request or handling a simple button click,
installing a heavy reactive library like RxJS is "overkill."
[Link] is mixing synchronous loops (like for..of) with asynchronous callbacks
so dangerous?
The Problem:
o The Hidden Bug: Synchronous loops do not care about network
latency. It does not pause the execution to wait for the ajax
background tasks to finish. The loop will fire off all the AJAX requests
almost simultaneously, instantly marching ahead to completion.
o The Consequences:
Unpredictable Execution Order: Because network speeds
fluctuate, the data for item #5 might return and get processed
before item #1.
Race Conditions & Shared State Bugs: If these asynchronous
steps share or modify any global variables, the out-of-order
responses will corrupt your application's state.
E.g. – In what order will these messages print to the console?
Items loaded then 2. Render complete
Render complete then 1. Items loaded
They will print at the exact same millisecond.
It depends entirely on how fast the user's computer
processor is.
Answer is B - Because ajax is asynchronous and non-
blocking, JavaScript fires off the request and
immediately moves to the next line. Therefore,
beginUiRendering() executes and prints "2. Render
complete" first. Only later, when the server responds,
does the callback execute to print "1. Items loaded."
[Link] is an Event Emitter, and how does it work?
An Event Emitter is a popular mechanism used to build asynchronous,
event-based architectures
The DOM is the most famous example of an event emitter (listening for
mouse clicks or key presses).
E.g. - Consider a simple calculator object that can emit events like add and
subtract, which you can hook any custom logic into;
Triggering the flow
What happens when you run this:
o [Link]('add', 2, 3) sends the arguments 2 and 3 down the add
channel.
o The runtime intercepts this request and immediately triggers the
associated Adder function.
o The custom logic finishes processing the numbers and passes the
data return (5 or -1) straight back to the terminal listener.
The Flow of an Event Emitter Model
o Event Publication: The Calculator component acts as the publisher. It
establishes and exposes a specific set of public event channels—in
this case, Add and Subtract.
o Event Invocation (emit): A Client fires an execution request by
emitting a specific event name along with arguments, such as passing
the inputs via emit(2, 3).
o Logic Execution: The moment the emitter registers that a specific
channel is fired, the runtime system immediately intercepts it and
executes the corresponding custom logic block mapped to that
specific channel (like the Adder function or Minus function).
o Data Return: Once the logic block processes the inputs, the calculated
result value (5 for addition or -1 for subtraction) is passed directly
back down to the handler interface.
[Link] is the fundamental difference between Callbacks and Event
Emitters?
The primary distinction comes down to the cardinality of the
communication: Callbacks are one-to-one, whereas Event Emitters are one-
to-many.
Callbacks: One-to-One (1:1)
o A callback is a direct invocation between a single caller and a single
handler.
o The Mechanism: You pass exactly one function down into a task (like
an HTTP request). When that task finishes, it executes that specific
function.
o Limitation: You cannot naturally attach a second or third independent
callback to that same running task without modifying the function
itself. It is a closed, private relationship.
o Analogy: Ordering a single custom item at a store and leaving your
phone number. When it's ready, they call only you.
o Example - If we designed the calculator using a traditional callback,
the relationship between the trigger and the receiver is strictly
private and exclusive.
o Why this is One-to-One (1:1):
Exclusive Channel: You pass exactly one handler function into
the calculation.
No Room for Others: If your analytics tracker or system logger
also wants to know when an addition happens, you cannot
easily plug them in. You would have to modify the callback
itself, making the code tightly coupled and messy.
Analogy: Sending a private text message to one specific friend.
Only they get the message.
Event Emitters: One-to-Many (1:N)
o An Event Emitter acts as a central broadcasting hub. It allows an
application to decouple the source of an action from the elements
responding to it.
o The Mechanism: An object broadcasts (emits) a named event out into
the open. Multiple separate, completely unrelated areas of your code
can choose to listen to that exact same event independently.
o Benefit: You can easily add new listeners at any time without altering
the source code that fires the event.
o Analogy: A radio station broadcasting a signal. The station doesn't
know or care who is listening; five different people can tune their
radios to the same frequency and react to the music independently.
o My using the Event Emitter pattern from your diagram, we decouple
the calculator from its consumers. The calculator simply broadcasts
that an event happened, and any number of independent
components can listen and react.
o The Code:
Why this is One-to-Many (1:N):
o Decoupled Broadcast: The calculator has absolutely no idea who is
listening. It simply shouts: "I am running an 'add' event with 2 and 3!"
o Unlimited Subscriptions: Multiple completely unrelated parts of your
codebase (UI, Analytics, Logger) can tune in to that exact same 'add'
event. They all execute their custom logic independently when that
single emit is fired.
o Analogy: A radio station broadcasting music. The station plays the
song once, and hundreds of radios tune in and play it simultaneously.
[Link] is the main drawback of Event Emitters when dealing with complex
asynchronous logic?
While Event Emitters are incredibly powerful for decoupling code, they
suffer from the exact same nesting and composition issues as traditional
callbacks.
When you need to coordinate or sequence multiple events coming from
different resources (e.g., waiting for Event A from a file reader AND Event B
from a database before executing Action C), you quickly descend back into
hard-to-read, nested callback logic. They do not naturally compose
together.
The Modern Evolution: To fix these composition nightmares, the JavaScript
community turned to functional programming patterns. This led to
Promises in ES6, and eventually, the highly flexible stream pipeline system
of RxJS Observables.
[Link] were Promises introduced over Callbacks and Event Emitters?
While callbacks and event emitters were the original tools for handling
asynchronous JavaScript, they couldn't scale cleanly when applications grew
in complexity.
Promises were introduced in ES6 to solve three major paint points:
o Unifying Two Split Systems:
Previously, developers had to mix
callbacks/Promises for single-value actions (like an HTTP
fetch) and
Event Emitters for multi-value streams (like clicks).
This mismatch resulted in fractured, confusing code.
Promises offered a standard, language-level data type to wrap
asynchronous operations.
o Fixing the Control Flow (Callback Hell):
Traditional callbacks and event emitters required deep
horizontal nesting to ensure tasks happened in a specific order.
Promises flattened this structure entirely into clean, top-to-
bottom .then() chains.
o Centralized Error Handling:
Instead of writing tedious error-checking logic inside every
single nested layer of a callback or event listener, Promises
introduced the .catch() block, allowing a single line of code at
the end of a chain to handle a failure from any step prior.
[Link] is a "Continuation" and "Continuation-Passing Style" (CPS)? (With
Examples)? Issue with CPS?
Continuation: A callback function that explicitly dictates what the program
should do next with a future value, instead of making the system freeze and
wait for a traditional return value.
Continuation-Passing Style (CPS): A style of programming where functions
never return values using the return keyword. Instead, they accept a
continuation (a callback) as an argument and pass control forward by
invoking that continuation with the computed result.
Example –
o Direct Style (Traditional Synchronous Return)
The function calculates a value and immediately hands it back
to the caller using a standard return statement.
o Continuation-Passing Style (CPS) -
The function never passes a value back up the stack. Instead, it
expects a continuation function and pushes the control flow
forward
Issue with CPS:
o When you try to chain multiple CPS functions together in traditional
JavaScript, you are forced to nest them sequentially. This creates a
deeply indented chain of commands where the output of one step
becomes the input to the next:
o Why This Design Fails at Scale:
Temporal Dependency: Step 2 requires data from Step 1, and
Step 3 requires data from Step 2. They are tightly bound by
time.
Callback Hell: Because they depend on each other, they must
nest inward. Adding more steps causes the code to grow
horizontally into an unreadable "Pyramid of Doom."
Fractured Error Handling: There is no error catching here. To
add it, you would have to manually add error arguments and if
(err) blocks inside every single layer, creating a maintenance
nightmare.
o Promises turn these raw, heavily nested continuations into first-class
citizens by explicitly defining what it means to "continue" using
flat .then() statements.
[Link] do Promises improve upon traditional callbacks?
A Promise is an immutable data type introduced in ES6 that wraps a long-
running or asynchronous operation, allowing you to subscribe to its future
result or error.
Promises improve upon callbacks in three major ways:
o Linear Readability: They convert deep horizontal nesting into a
readable, top-to-bottom sequence using .then() blocks ("Do X, then
do Y").
o Declarative Control Flow: By splitting your steps into independent,
reusable functions, you can stitch them together cleanly. For
example, [Link]() allows you to map an array of items into
separate promises and wait for them collectively.
o Centralized Error Handling: Instead of placing individual error checks
inside every single callback level, you can append a .catch() block at
the end of the chain to gracefully catch any failure that occurs along
the way.
Example - Promises flatten and improve that exact nested callback example
by turning it into a clean, linear, top-to-bottom chain.
3 Ways Promises Drastically Improve the Code
o Eliminates Callback Hell (Flattens Structure): Instead of nesting
functions horizontally inside one another, Promises use .then() to
create a clean, vertical sequence. It reads like plain English: Fetch the
user, then get their orders, then get the shipping status.
o Clean Error Handling: Instead of writing complex if (err) checks at
every single level, you attach a single .catch() block at the very end. If
any operation fails at any point in the chain, execution stops
immediately and jumps straight down to the catch handler.
o Maintains Open State Control: Because each .then() returns a brand
new Promise representing the next future value, you aren't trapped
inside a locked closure. The functions are decoupled, making it much
easier to intercept, modify, or insert new steps later.
[Link] are the three major limitations of Promises?
Despite their advantages over raw callbacks, Promises have distinct
architectural constraints that make them insufficient for complex
architectures:
o Single-Value Only: A Promise can only handle data sources that
produce a single value and then close (like an HTTP request). They are
completely incapable of handling continuous streams of data, such as
mouse movements or sequences of bytes in a file stream.
o Cannot Be Cancelled: Once a Promise begins executing, it cannot be
aborted. Even if the underlying mechanism (like a browser
XMLHttpRequest) supports cancellation, the Promise interface does
not honor it. If a user navigates away from a page, the network
request will wastefully run to completion anyway.
o No Native Retry Logic: If an asynchronous operation fails due to a
temporary network glitch, a Promise lacks the built-in capability to
automatically retry the operation.
[Link] vs. Event Emitters: Why do they cause disjointed code?
Because of their technical limitations, developers are frequently forced to
mix both paradigms in a single application:
o Promises are used for single-value returns (like hitting an API
endpoint).
o Event Emitters are used for multi-value streams (like handling
continuous UI mouse clicks).
o Mixing these two completely different mechanisms to achieve a
single business goal often results in disjointed, confusing, and
fractured codebases.
Enter RxJS: RxJS completely unifies these concepts. An Observable handles
both single values (like Promises) and infinite streams of values (like Event
Emitters) under a single, unified blueprint that supports native cancellation,
automatic retries, and powerful stream manipulation operators.
PART II –
1. What is RxJS and what are its core pillars?
RxJS (Reactive Extensions for JavaScript) is a library designed replacement
for traditional callback and Promise-based code.
It is built on two primary pillars:
o Functional Programming (FP): Using pure, reusable functions to
transform data without side effects.
o Reactive Programming (RP): Writing code that is built around reacting
to incoming events or data changes over time.
2. What does it mean to "think in streams"?
In RxJS, everything is treated as a data stream. Whether your data is a single
static number, a slow HTTP call, or thousands of rapid mouse clicks, RxJS
processes them using the exact same stream blueprint.
💡 Array vs. Stream
o Array (Static): You have a box of numbers all at once.
o Stream (Over Time): You have a pipe where numbers drop out one by
one over time.
3. What are the 4 fundamental components of an RxJS stream?
Every stream is built from these four parts:
o Producer (Observable): The source that pushes out data on a "fire-
and-forget" basis (e.g., keystrokes, intervals, or file readers).
o Consumer (Observer): The listener at the end of the line that reacts
to the received data.
o Data Pipeline: The operators (like map or filter) that clean and shape
the data while it is traveling from the producer to the consumer.
o Time: The invisible engine behind the stream. Time allows you to
slow down clicks (debounce), delay events, or speed them up.
💡 A Water Filtration System
o Producer: The main water utility pipe (Observable) pushing raw
water.
o Pipeline: The physical water filter (Operators like filter) purifying the
water mid-transit.
o Consumer: Your glass (Observer) catching the clean water at the
kitchen sink.
4. What does "Upstream" and "Downstream" mean?
Data in a stream is strictly a one-way street; it always travels from the
producer down to the consumer.
Upstream: Where the events are born (e.g., a keyboard element producing
typing events).
Downstream: Where the logic acts on those events (e.g., code displaying
the text on a screen).
Because they are loosely coupled, you can change your downstream
rendering code without ever touching your upstream keyboard events.
Example –
5. How does RxJS solve nested asynchronous flows better than Promises?
When you have steps that depend on each other (e.g., Get User Get
their Playlist Get their Songs), callbacks nest horizontally, and Promises
require complex array mapping and [Link] wrappers.
RxJS flattens this beautifully. Using stream-mapping operators, it turns a
complex nested sequence into a single, elegant pipeline that ends in one
simple .subscribe() block.
Example: Chaining Requests
6. How do Object-Oriented, Functional, and Reactive Programming differ in
what they focus on?
Every programming paradigm changes the central "building block" you use
to reason about your code:
o Object-Oriented Programming (OOP): Focuses on Objects and State.
It places the data (state) inside objects, and the complexity comes
from how these objects interact with each other.
o Functional Programming (FP): Focuses on Behavior and Functions.
Functions are the main units of work, processing data cleanly without
mutating it.
o Reactive Programming (RP): Focuses on Streams of Change. Instead
of holding data in static boxes, it views data as a constantly flowing
river of events moving through time.
7. What does it mean that state is "transient" in Reactive Programming?
In traditional OOP, data is stored long-term inside variables or arrays
(monolithic collections).
In Reactive Programming, data is transient, meaning it is temporary and
strictly in transit.
Data is never permanently parked inside a variable; it flows straight through
the stream pipeline directly to the listener that is currently subscribed to it.
This makes your event handling much easier to track, test, and reason
about.
8. What is the difference between Imperative (OOP) and Declarative
(RxJS/FP) styles? OOP vs. RxJS Paradigm
Imperative Style (How): You write a manual, step-by-step instruction list
telling the computer exactly how to manage variables, track loop progress,
and mutate state to reach a solution.
Declarative Style (What): You write code that clearly states what you want
to achieve. You stitch together a pipeline of logic that reads smoothly, like a
spoken sentence (e.g., "Filter out items, then map them, then notify me"),
without manually managing temporary variables or worrying about
accidentally messing up outside state.
Paradigm of OOP vs. RxJS
o Let's look at the exact same task implemented in both styles so you
can see the mental shift.
o The Task:
A user is clicking a button. We want to count the clicks, but we
only care about even-numbered clicks (2nd click, 4th click, 6th
click, etc.), and we want to print the click count multiplied by
10.
The Declarative RxJS Way (Focuses on Streams of Change)
o Here, there is no global state variable to maintain or accidentally
corrupt.
o The clicks flow down a pipeline like water. We simply declare what
operations should happen to the data as it passes through.
o Advantage –
No Side Effects:
The scan, filter, and map functions are pure.
They don't touch or mutate any variables outside
themselves.
Readability:
The code reads smoothly from top to bottom like a
human sentence describing what it does.
Combining Paradigms:
You can perfectly use OOP to build your application
objects, and use Functional Reactive Programming (FRP)
via RxJS to drive the background asynchronous events
and data changes cleanly across those objects.
9. What is the main "unit of work" and driver of logic in Object-Oriented
Programming (OOP) VS RXJS?
OOP –
o In Object-Oriented Programming (OOP), you build your code around
Classes, which act as blueprints for creating objects. Think of a class
as a container that holds two things:
Properties (State): The data or information stored inside the
object.
Methods (Interactions): Functions that operate on that data.
o The fundamental idea of OOP is that an application progresses
by mutating (changing) an object's internal state through its
methods.
💡 The Online Banking Example (OOP): An online banking
website is built out of domain models:
Properties (State): Represented by domain models that
store and manage structural data
Account details
Account balance
Account Number
User profiles
Customer information
Methods (Interactions):
Represented by the business logic actions you
perform on that state, such as
withdraw() - withdrawing,
deposit() - depositing, and
transfer() - transferring money.
The State:
Suppose an account starts with: A variable inside the
Account class holds a static number:
balance = 100;
When the user deposits ₹50:
deposit(50);
The Mutation: the method directly changes the stored value:
When you deposit money, a method manually changes
that exact variable to
100 + 50 = 150.
The object itself owns the data, and its methods
continuously modify that same state over time.
Example 1 -
Example 2 –
RxJS (Reactive Programming) –
o In RxJS, the primary unit of work is the Observable (Stream) rather
than a class.
o Instead of organizing logic around objects that hold mutable state,
RxJS organizes logic around streams of values emitted over time.
o An Observable represents a sequence of events such as:
Mouse clicks
API responses
User input
Timer ticks
WebSocket messages
o Rather than manually changing variables, you create a pipeline that
transforms incoming data using operators.
o Common operators include:
map()
filter()
switchMap()
mergeMap()
scan()
o The application moves forward because new values are emitted
through streams, not because existing objects are constantly
mutated.
o 💡 Online Banking Example (RxJS): Imagine every banking event is
emitted into a stream.
Deposit(50)
Withdraw(20)
Deposit(100)
Transfer(30)
o Instead of directly updating a balance variable, these events flow
through an Observable pipeline:
o The emitted balances become:
100
150
130
230
o Notice that the logic reacts to incoming events rather than repeatedly
modifying a shared object.
o Summary -
OOP: The application is driven by objects and mutable state.
Classes encapsulate data and behavior, and methods modify
the object's internal state.
RxJS: The application is driven by Observables and data
streams. Instead of mutating shared state, values flow through
a pipeline of operators, producing new outputs whenever new
events occur.
[Link] Programming as the Foundation of Reactive Programming?
Reactive Programming (RP) is built on top of Functional Programming (FP)
concepts. This means that before understanding RxJS, it helps to
understand the basic principles of functional programming.
Think of it like this:
Think of it like building a house.
Just like a house needs a strong foundation, Reactive Programming needs
Functional Programming.
Reactive Programming does not replace Functional Programming. It extends
it by adding support for asynchronous and event-driven data streams.
Most of the power of RxJS comes from functional programming ideas.
o For example, RxJS operators such as:
map()
filter()
reduce()
scan()
mergeMap()
switchMap()
are all inspired by Functional Programming.
Why is Functional Programming the Foundation?
o Instead of writing code that changes variables repeatedly, Functional
Programming focus on:
Transform data instead of changing it.
Write small reusable functions.
Avoid changing shared variables (mutable state).
Combine functions together.
o Reactive Programming uses these same ideas but applies them to
data that arrives over time (events).
o Example –
o Notice:
We didn't change the original numbers.
We simply transformed each value.
This is Functional Programming.
Why is RxJS Called Reactive?
o Because it reacts whenever new data arrives.
o For example:
What is ReactiveX Built From?
o The ReactiveX website defines it as:
ReactiveX is a combination of the best ideas from
the Observer pattern,
the Iterator pattern,
and Functional Programming.
RxJS combines three major concepts.
o Functional Programming
Transform values: Provides the way data is transformed.
Don't modify the original data: E.g. Each function receives data
and returns new data without modifying the original.
o Observer Pattern
One object produces data aka Producer.
Another object listens to it aka consumers.
Provides communication between producers and consumers.
The Observer receives values whenever the Observable emits
them.
E.g. – Button Click Observable Observer receive click
Other examples:
Mouse click
HTTP response
Timer
WebSocket message
Think: Observer answers: "How do I receive data?"
Iterator Pattern (Processes One Value at a Time)
o Purpose:
Handle items one by one.
o Traditional array:
RxJS extends this idea.
Instead of values already existing,
they may arrive later.
E.g. - [1,2,3] - Iterator
1
2
3
Iterator says: "How do I process each value?"
Putting Everything Together
Imagine a button click.
Instead of iterating through an array immediately:
an Observable may emit
Here,
o Observer receives the event.
o Functional Programming transforms the event.
o Reactive Programming (RxJS) connects everything together.
Easy Memory Trick
o Remember this formula:
Reactive Programming
=
Functional Programming
+
Observer Pattern
+
Iterator Pattern
o Or simply:
o FP → transforms data
o Observer → receives data
o Iterator → processes data
o Reactive → combines all three
[Link] is the Stream's Data-Driven Approach in RxJS, and how does it
separate business logic from the data source?
OR
How does RxJS separate business logic from the data source, and why is
this called a data-driven approach?
Stream's Data-Driven Approach
o What is Data-Driven Programming?
Definition:
Data-Driven Programming means writing your business
logic once and letting different types of data flow
through it.
Instead of writing separate code for arrays, button clicks,
HTTP responses, or WebSocket messages, RxJS treats all
of them as streams of data.
Traditional OOP Approach -
o In Object-Oriented Programming (OOP), developers spend a lot of
time choosing the right data structure before writing business logic.
o For example, in Java, if you want to store multiple values, you must
decide:
Array
ArrayList
LinkedList
DoublyLinkedList
ConcurrentLinkedList
o Each collection has different methods and behavior.
o So, the focus becomes:
Choose the structure
↓
Write business logic
o The structure often dictates how you write your code.
RxJS (Data-Driven Approach)
o RxJS changes this mindset.
o It says:
Don't worry about where the data comes from. Just focus on what
you want to do with the data.
o Whether the data comes from:
an Array
a Button Click
an HTTP API
a Timer
a WebSocket
RxJS treats them all as Observables (Streams).
o Your processing logic stays almost the same.
Any Data Source
│
▼
Observable(Stream)
│
▼
filter()
map()
reduce()
switchMap()
│
▼
Result
o Example
Suppose your business logic is:
Take even numbers.
Square them.
Print the result.
o Data Source 1: Array
MyNumbers
.filter(isEven)
.map(square);
o Data Source 2: Button Clicks
fromEvent(button, 'click')
.pipe(
filter(...),
map(...)
);
o Data Source 3: HTTP Response
[Link](...)
.pipe(
map(...),
filter(...)
);
o Notice that only the data source changes.
o The processing logic remains almost identical.
o This is called Data-Driven Programming.
Florist Example (From the Book)
o Imagine you own a flower shop.
Your real business is:
Buying flowers
Cutting flowers
Packaging bouquets
Taking customer orders
Delivering flowers
o These activities make money.
Now imagine someone asks you to design and build your own
delivery truck.
That is not your real business.
It distracts you from what actually matters.
OOP Analogy
o In OOP, developers sometimes spend too much time deciding:
Which collection class?
Which data structure?
Which implementation?
o Instead of solving the business problem.
RxJS Analogy
o RxJS says:
"Forget about designing the truck."
Just receive the flowers (data),
process them,
and send them to the customer.
Your business logic stays the same.
Separation of Behavior from Data
o This is the most important sentence in the chapter.
o RxJS separates:
Data
──────────────
Array
Mouse Click
HTTP Response
Timer
WebSocket
From
Behavior
──────────────
filter()
map()
reduce()
scan()
switchMap()
o They are independent.
o Any data can pass through the same behavior.
Why is this Useful?
o Suppose today you're reading data from an Array.
o Tomorrow your manager says,
o "Now fetch it from an API."
o In traditional code, you may rewrite a lot.
In RxJS,
o only the source changes.
Array
│
▼
map()
filter()
Today
HTTP
│
▼
map()
filter()
Tomorrow
WebSocket
│
▼
map()
filter()
The processing pipeline remains the same.
Data-Driven Programming in RxJS means separating business logic from the
data source. Instead of writing different code for arrays, button clicks, HTTP
responses, or WebSocket messages, RxJS converts all of them into
Observables (streams). The same operators like map(), filter(), and reduce()
can then process any stream of data. This makes the code reusable,
consistent, and easier to maintain.
[Link] is the Data-Centric (Data-Driven) approach in RxJS, and how does it
differ from the Object-Oriented approach? why RxJS separates data from
behavior?
Data-Centric (Data-Driven) Design in RxJS
o Main Idea
In RxJS, data drives the application. Behavior (business logic)
runs only when data arrives.
Without data, nothing happens.
Think of it like this:
No Data
│
▼
Observable (Idle)
│
No Processing
When data arrives:
Data Arrives
│
▼
Observable
│
▼
map()
filter()
scan()
│
▼
Output
The data activates the behavior.
OOP vs Data-Driven Design
o OOP
o In OOP, data and behavior are tightly coupled inside an object.
Account Object
----------------------
balance
accountNo
deposit()
withdraw()
transfer()
The object owns both the data and the methods.
RxJS
o In RxJS, data is separate from behavior.
Data
(Click, API, Timer)
│
▼
Observable
│
▼
map()
filter()
scan()
│
▼
Result
The data flows through reusable functions.
Why Separate Data from Behavior?
o Suppose today your data comes from:
Array
>> Tomorrow it comes from:
HTTP API
>> Next week it comes from:
WebSocket
Your processing logic remains exactly the same.
Any Data Source
│
▼
Observable
│
▼
map()
filter()
scan()
│
▼
Output
This makes the application more reusable and easier to maintain.
Real-Life Example (Physics)
o The book gives a simple analogy. A ball has a mass.
Mass = 2 kg
This number alone means nothing. Only when gravity acts on it
Gravity
does the ball fall.
o Similarly, Data alone has no meaning until behavior acts on it. RxJS
simply keeps the data and behavior separate.
o Producer–Consumer Model RxJS follows a Producer–Consumer
model
Producer
(Button Click)
│
▼
Observable
│
▼
map()
filter()
buffer()
│
▼
Consumer
(subscribe())
o The producer creates data.
o The pipeline transforms data.
o The consumer uses the final result.
o Each part has a single responsibility.
Example –
Stream([1,2,3,4,5,6])
.buffer(2)
.subscribe([Link]);
Output:
[1,2]
[3,4]
[5,6]
Notice:
o buffer() only groups values.
o subscribe() only consumes values.
Each step has one job.
This is called Separation of Concerns.
Different Producers, Same Processing
o Normally we think every data source needs different code.
RxJS says:
o Treat everything as an Observable.
Array
Button Click
HTTP
Timer
WebSocket
│
▼
Observable
│
▼
Same Operators
(map, filter, scan...)
One programming model works for all.
RxJS follows a data-driven or data-centric approach, where data is
separated from behavior. Instead of embedding business logic inside
objects, data flows through an Observable pipeline of reusable operators
like map(), filter(), and buffer(). The pipeline remains idle until data arrives,
making the application reactive. Different data sources such as arrays,
button clicks, HTTP responses, timers, and WebSockets are all treated as
Observables, allowing the same business logic to process any type of data.
This separation makes the code more modular, reusable, and easier to
maintain.
[Link] is the purpose of wrapping data sources with [Link]?
An Observable is the core data type in RxJS.
(stream that we discuss are pseudo datatype. That term was only use for oversimplication of topic).
[Link] wraps different kinds of data sources—
o such as arrays,
o promises,
o events (mouse clicks, keyboard input),
o timers (setTimeout, setInterval),
o generators, and
o HTTP responses—into a common Observable type.
No matter where the data comes from, RxJS converts it into a single type
called Observable
Once RxJS wraps all datasource, inside an Observable, then it can be
processed through the same reactive pipeline.
Pipeline consist of RxJS operator like
o map(),
o filter(),
o scan(),
o switchMap(),
o reduce()
o mergeMap()
E.g. - Different Data Sources
Array, Promise, Button Click, HTTP Response, Timer, WebSocket, Generator
│
▼
Wrap inside [Link]
│
▼
map(), filter(), scan(), switchMap(), reduce()
│
▼
Processed Output
It allows you to wrap different kinds of data sources into a single
programming model. It handles all kinds of data.
Types of Data Sources: From a data-driven perspective, RxJS groups data
into three categories.
o Emitted Data
o Static Data
o Generated Data
Emitted Data:
o Definition - Emitted data is produced as a result of some interaction
with the system. The data does not exist immediately; it arrives
sometime in the future.
o Examples:
Mouse clicks
Keyboard input
HTTP responses
File read events
WebSocket messages
o Characteristics:
Arrives asynchronously.
May emit one value or many values.
The application reacts whenever new data is emitted.
o Promise vs Observable
Promise → Suitable for one future value (e.g., a single HTTP
response).
Observable → Suitable for multiple values over time (e.g.,
mouse clicks, keyboard events, WebSocket messages).
Static Data:
o Definition: Static data is data that already exists in memory before
the program begins processing it.
o Examples:
Arrays
Strings
Maps
Test data
o Example:
const numbers = [1,2,3,4];
When RxJS wraps an array inside an Observable, it does not
store another copy of the array.
Instead, it uses iterators to emit each element one by one.
Array
[1,2,3,4]
│
Observable
│
1→2→3→4
This allows arrays to be processed just like any other stream of
data.
Generated Data:
o Definition: Generated data is data that is created periodically or on
demand instead of already existing in memory.
o Examples:
Clock ticks
setTimeout()
setInterval()
Fibonacci sequence
ES6 Generators
Some generated data is infinite.
o For example, the Fibonacci sequence could continue forever.
o Instead of storing all values in memory, each value is generated only
when needed.
o Generator
1 --> 1 --> 2 --> 3 --> 5-->8-->13-->...
This makes generated data memory-efficient and suitable for
streaming.
[Link] wraps different kinds of data sources—such as arrays,
promises, events, timers, generators, and HTTP responses—into a common
Observable type. This allows all data sources to be processed using the
same reactive programming model and the same set of RxJS operators,
resulting in more reusable, consistent, and maintainable code.
[Link] are RxJS Observables created, and how do they use operator
chaining to process data?
Observable as the core data type.
o An Observable ([Link]) is the core data type in RxJS. It acts as
a Producer, which emits data (notifications/events) over time.
o An Observer is the Consumer that subscribes to the Observable and
receives those emitted values.
o RxJS follows the Observer Pattern, where:
Observable → Producer (Something that can be observed)
Observer → Consumer (Observes the producer)
Observer (Subscribes)
│
▼
Observable (Producer)
│
Emits notifications/events
▼
Observer reacts
Observer subscribes to an Observable.
o Observable Pushes Notifications
o An [Link] pushes notifications (values/events) to its
subscribed Observers.
o Whenever the Observable emits a new value, the Observer
automatically reacts to it.
o This asynchronous communication allows applications to remain
responsive instead of blocking while waiting for events.
o It is ideal for building asynchronous and responsive applications on
both the client and the server.
Observable represents present and future values.
o An Observable does not only represent data that exists now. But, It
can also represent data that will occur in the future.
o Examples:
Array → Value exists now.
HTTP Response → Value arrives later.
Mouse Click → Values are produced whenever the user clicks.
Timer → Values are produced in the future.
o Therefore, an Observable represents a stream of current or future
values.
Operator chaining (map(), filter(), switchMap(), etc.).
o Observables support method chaining, where each operator
transforms the data before passing it to the next operator.
o Example
[Link](dataSource)
.operator1()
.operator2()
.operator3()
.subscribe(processOutput);
o Flow:
Data Source
│
▼
Observable
│
▼
Operator 1
│
▼
Operator 2
│
▼
Operator 3
│
▼
subscribe()
│
▼
Final Result
o The subscriber only receives the final transformed output.
Immutability of Observables.
o Observables are immutable.
o This means operators do not modify the existing Observable.
o Instead, each operator creates and returns a new Observable.
Observable
│
▼
map()
│
▼
New Observable
│
▼
filter()
│
▼
New Observable
o This makes Observable pipelines predictable, reusable, and easy to
maintain.
Lazy execution with subscribe():
o Observables are lazy.
o Nothing executes until an Observer subscribes.
o const stream = [Link]([1,2,3])
.map(x => x * 2);
o Nothing happens here.
o Execution starts only after:
[Link]([Link]);
o The Observer then begins receiving emitted values.
Just like a house needs a strong foundation, Reactive Programming needs
Functional Programming.
Just like a house needs a strong foundation, Reactive Programming needs
Functional Programming.
15.s
Just like a house needs a strong foundation, Reactive Programming needs
Functional Programming.