What is subscribe() in RxJS?
1. What is subscribe()?
subscribe() is an RxJS method used to start listening to an Observable and receive the values it emits.
When you subscribe, you can handle three types of notifications:
next— receives emitted values.error— receives an error if the Observable fails.complete— runs when the Observable finishes successfully.
Simple definition
subscribe()connects a consumer to an Observable so the consumer can receive the Observable's emitted values, errors, and completion notification.
Basic syntax:
observable.subscribe({
next: value => {
// Handle value
},
error: error => {
// Handle error
},
complete: () => {
// Handle completion
}
});
2. Full Simple Sample Program
Let's first understand subscribe() without an HTTP API.
app.ts
import { Component } from '@angular/core';
import { Observable } from 'rxjs';
@Component({
selector: 'app-root',
standalone: true,
templateUrl: './app.html'
})
export class App {
messages: string[] = [];
constructor() {
const message$ = new Observable<string>(subscriber => {
subscriber.next('Hello');
subscriber.next('Welcome to Angular');
subscriber.next('Learning RxJS');
subscriber.complete();
});
message$.subscribe({
next: (value) => {
console.log('Value:', value);
this.messages.push(value);
},
error: (error) => {
console.error('Error:', error);
},
complete: () => {
console.log('Observable completed');
}
});
}
}
app.html
<h2>Messages</h2>
@for (message of messages; track $index) {
<p>{{ message }}</p>
}
Output
Hello
Welcome to Angular
Learning RxJS
Console:
Value: Hello
Value: Welcome to Angular
Value: Learning RxJS
Observable completed
3. How Does subscribe() Work?
Look at this code:
const message$ = new Observable<string>(subscriber => {
subscriber.next('Hello');
subscriber.next('Angular');
subscriber.complete();
});
We have created an Observable.
Then:
message$.subscribe({
next: value => console.log(value)
});
connects the consumer to that Observable.
The flow is:
Observable
│
│
├── next("Hello")
│
├── next("Angular")
│
└── complete()
│
▼
subscribe()
│
├── next
├── error
└── complete
Think of:
Observable = Producer
subscribe() = Start listening
next() = Receive value
error() = Receive failure
complete() = Stream finished
4. What is next?
next handles each value emitted by the Observable.
Producer:
subscriber.next('Hello');
subscriber.next('Angular');
Consumer:
message$.subscribe({
next: value => {
console.log(value);
}
});
Output:
Hello
Angular
Flow:
Observable Subscriber
next("Hello") ────────────► next handler
next("Angular") ────────────► next handler
Every time the Observable emits a value, the next callback runs.
5. What is error?
error handles failures from the Observable.
Example:
const data$ = new Observable<string>(subscriber => {
subscriber.next('Processing...');
subscriber.error('Something went wrong');
});
Subscribe:
data$.subscribe({
next: value => {
console.log(value);
},
error: error => {
console.error(error);
}
});
Output:
Processing...
Something went wrong
Once error() occurs, that Observable execution terminates.
For example:
subscriber.next('One');
subscriber.error('Error');
subscriber.next('Two');
"Two" will not be delivered.
6. What is complete?
complete tells the subscriber:
The Observable has finished successfully and will not emit any more values.
Example:
const numbers$ = new Observable<number>(subscriber => {
subscriber.next(10);
subscriber.next(20);
subscriber.next(30);
subscriber.complete();
});
Subscribe:
numbers$.subscribe({
next: value => {
console.log(value);
},
complete: () => {
console.log('Completed');
}
});
Output:
10
20
30
Completed
Flow:
10
↓
20
↓
30
↓
complete
↓
STOP
7. next vs error vs complete
| Handler | Purpose | Can occur multiple times? |
|---|---|---|
next |
Receives values | Yes |
error |
Handles failure | No, terminates stream |
complete |
Successful completion | No, terminates stream |
An Observable execution can end with either:
next → next → next → complete
or:
next → next → error
It cannot continue emitting after error or complete.
8. Real Angular Example — HttpClient
This is where you will commonly use subscribe().
Service
import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
export interface Product {
id: number;
name: string;
price: number;
}
@Injectable({
providedIn: 'root'
})
export class ProductService {
private http = inject(HttpClient);
getProducts(): Observable<Product[]> {
return this.http.get<Product[]>(
'https://localhost:7001/api/products'
);
}
}
Component:
this.productService.getProducts()
.subscribe({
next: (products) => {
this.products = products;
},
error: (error) => {
console.error('API Error:', error);
},
complete: () => {
console.log('HTTP request completed');
}
});
9. What Happens Step-by-Step?
When Angular executes:
this.productService.getProducts()
it gets:
Observable<Product[]>
Then:
.subscribe(...)
subscribes to it.
The flow is:
Component
│
│ getProducts()
▼
ProductService
│
▼
HttpClient.get()
│
▼
Observable<Product[]>
│
│ subscribe()
▼
HTTP GET request
│
▼
ASP.NET Core Web API
│
▼
JSON Response
│
▼
next(products)
│
▼
this.products = products
│
▼
complete()
For a normal Angular HTTP request, the Observable generally emits the response and then completes.
10. Does subscribe() Start the HTTP Request?
For Angular HttpClient, yes.
HttpClient returns a cold/lazy Observable.
Consider:
const products$ =
this.http.get<Product[]>('/api/products');
You have created the Observable, but the HTTP request is not normally sent yet.
When:
products$.subscribe(products => {
console.log(products);
});
the HTTP request is executed.
http.get()
│
▼
Observable created
│
│ subscribe()
▼
HTTP GET sent
│
▼
Server
This is an important interview point.
11. What Happens If We Don't Subscribe?
Consider:
const products$ =
this.http.get<Product[]>('/api/products');
but never:
products$.subscribe(...)
For a normal HttpClient Observable, the request is not executed.
So this:
getProducts(): Observable<Product[]> {
return this.http.get<Product[]>(this.apiUrl);
}
only returns the Observable.
The component (or another consumer) eventually subscribes:
this.productService
.getProducts()
.subscribe(...);
12. Multiple subscribe() Calls
Suppose:
const products$ =
this.http.get<Product[]>('/api/products');
Then:
products$.subscribe();
products$.subscribe();
Because Angular HttpClient Observables are cold, this can trigger two HTTP requests:
Observable
│
├── Subscription #1
│ │
│ └────► GET /api/products
│
└── Subscription #2
│
└────► GET /api/products
So don't assume multiple subscribers automatically share one HTTP request.
13. What Does subscribe() Return?
This is important.
subscribe() does not return the emitted data.
It returns a:
Subscription
For example:
const subscription =
observable$.subscribe(value => {
console.log(value);
});
The variable:
subscription
contains a Subscription, not the Observable's emitted value.
14. What is a Subscription?
A Subscription represents the active connection/execution between the Observable and its consumer.
const subscription =
observable$.subscribe(...);
You can later stop it:
subscription.unsubscribe();
Flow:
Observable
│
│
▼
Subscription
│
▼
Subscriber
unsubscribe()
│
▼
Stop receiving values
15. unsubscribe() Example
Consider a long-running Observable:
import { interval } from 'rxjs';
const timer$ = interval(1000);
const subscription = timer$.subscribe(value => {
console.log(value);
});
Output:
0
1
2
3
4
5
...
Later:
subscription.unsubscribe();
Then:
0
1
2
3
4
↑
unsubscribe()
STOP
No more values are delivered to that subscription.
16. Do We Need to Unsubscribe from Every HTTP Request?
Usually, not manually for a normal HttpClient request.
For example:
this.http.get<Product[]>(url)
.subscribe(products => {
this.products = products;
});
A normal HTTP Observable usually does:
subscribe
↓
HTTP request
↓
response
↓
next(response)
↓
complete
Once completed, the subscription is finished.
Long-lived Observables are more important to manage carefully, such as:
interval()
form.valueChanges
WebSocket streams
Custom Subjects
Event streams
In modern Angular, patterns such as AsyncPipe and takeUntilDestroyed() are often preferable for managing long-lived subscriptions.
17. subscribe() vs pipe()
This is another common confusion.
pipe()
Used to process/transform the Observable.
products$
.pipe(
map(products =>
products.filter(p => p.price > 1000)
)
);
subscribe()
Used to consume the final emitted values.
products$
.subscribe(products => {
console.log(products);
});
Together:
products$
.pipe(
map(products =>
products.filter(p => p.price > 1000)
)
)
.subscribe(products => {
console.log(products);
});
Flow:
Observable
│
▼
pipe()
│
▼
map()
│
▼
subscribe()
│
▼
Receive Data
Easy distinction:
pipe()processes the stream;subscribe()consumes the stream.
18. subscribe() vs next()
These are also very different.
next()
Means:
Produce/send a value.
subscriber.next('Hello');
subscribe()
Means:
Listen for values.
observable$.subscribe(value => {
console.log(value);
});
Think of:
Producer Consumer
next("Hello") ────────────────► subscribe()
│
▼
Hello
19. subscribe() and AsyncPipe
You don't always need to manually call subscribe() in Angular templates.
Suppose:
products$ =
this.productService.getProducts();
Template:
@for (product of products$ | async; track product.id) {
<p>
{{ product.name }}
</p>
}
Angular's AsyncPipe subscribes to the Observable for you and manages the subscription lifecycle.
Conceptually:
products$
│
▼
AsyncPipe
│
├── subscribe
│
├── receive values
│
└── manage cleanup
│
▼
Template
This is often cleaner when the Observable is consumed directly by the template.
Key Points
subscribe()is used to listen to an Observable.- It connects the Observable to a consumer.
nexthandles emitted values.errorhandles failures.completehandles successful completion.subscribe()returns a Subscription, not the emitted value.unsubscribe()stops an active subscription.- Angular
HttpClientObservables are lazy/cold; subscribing triggers the HTTP request. - Multiple subscriptions to an
HttpClientObservable can trigger multiple HTTP requests. - Normal HTTP Observables generally emit one response and complete.
- Long-lived Observables require more attention to subscription cleanup.
pipe()processes an Observable;subscribe()consumes it.next()produces a value;subscribe()listens for values.- Angular
AsyncPipecan subscribe and manage cleanup automatically in templates.
Easy way to remember
Observable = Data stream
│
▼
pipe() = Process data
│
▼
subscribe() = Start listening / consume
│
├── next → Got data
├── error → Something failed
└── complete → Finished
Interview Questions and Answers
1. What is subscribe() in RxJS?
Answer:subscribe() connects a consumer to an Observable so that it can receive emitted values, errors, and completion notifications.
2. What are the handlers in subscribe()?
Answer:
observable$.subscribe({
next: value => {},
error: error => {},
complete: () => {}
});
next handles values, error handles failures, and complete handles successful completion.
3. What does subscribe() return?
Answer:
It returns a:
Subscription
The Subscription can be used to stop the subscription:
subscription.unsubscribe();
4. Does subscribe() return the actual data?
Answer:
No. subscribe() returns a Subscription. The actual data is received inside the next handler.
observable$.subscribe({
next: data => console.log(data)
});
5. Does Angular HttpClient execute without subscribe()?
Answer:
Normally no. HttpClient returns a cold Observable, so the request is triggered when something subscribes to it, either explicitly with subscribe() or through a consumer such as AsyncPipe.
6. What happens if we subscribe twice to an HTTP Observable?
Answer:
Each subscription can trigger a separate HTTP request because Angular HttpClient Observables are cold.
7. What is the difference between pipe() and subscribe()?
Answer:pipe() is used to apply RxJS operators and process the Observable stream, while subscribe() consumes the stream and receives its values.
8. What is the difference between next() and subscribe()?
Answer:next() emits a value from a producer, while subscribe() listens for values emitted by an Observable.
9. Do we always need to unsubscribe?
Answer:
Not usually for normal HttpClient requests because they normally complete after receiving the response. Long-lived streams such as interval(), valueChanges, WebSockets, and custom Subjects require more careful lifecycle management.
10. Short Interview Answer
subscribe()is the RxJS method used to start consuming an Observable. It lets us handle emitted values throughnext, failures througherror, and successful completion throughcomplete, and it returns aSubscriptionthat can be unsubscribed when necessary.