← Back to Articles         
What is subscribe()?

What is subscribe()?

Published on 01 Oct 2026     9 min read Angular
RxJS & Observables

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.
  • next handles emitted values.
  • error handles failures.
  • complete handles successful completion.
  • subscribe() returns a Subscription, not the emitted value.
  • unsubscribe() stops an active subscription.
  • Angular HttpClient Observables are lazy/cold; subscribing triggers the HTTP request.
  • Multiple subscriptions to an HttpClient Observable 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 AsyncPipe can 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 through next, failures through error, and successful completion through complete, and it returns a Subscription that can be unsubscribed when necessary.

← Back to Articles