← Back to Articles         
Observable vs Promise — what is the difference?

Observable vs Promise — what is the difference?

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

Observable vs Promise — What Is the Difference?

Both Observable and Promise are used to handle asynchronous operations in JavaScript/Angular, but they work differently.

The easiest distinction is:

Promise represents one future result. Observable represents a stream that can produce zero, one, or multiple values over time.


1. Full Simple Sample Program

First, compare them with simple examples.

Promise Example

const promise = new Promise<string>((resolve, reject) => {

  setTimeout(() => {
    resolve('Product loaded');
  }, 2000);

});

promise.then(result => {
  console.log(result);
});

After 2 seconds:

Product loaded

A Promise settles once.


Observable Example

import { Observable } from 'rxjs';

const observable$ = new Observable<number>(subscriber => {

  subscriber.next(10);
  subscriber.next(20);
  subscriber.next(30);

  subscriber.complete();

});

observable$.subscribe({

  next: value => {
    console.log(value);
  },

  error: error => {
    console.error(error);
  },

  complete: () => {
    console.log('Completed');
  }

});

Output:

10
20
30
Completed

Here, the Observable emitted multiple values.


2. Main Difference

Promise

Start
  │
  │
  └────────────► One Result
                  │
                  ▼
                Done

Observable:

Observable

Start
  │
  ├────► Value 1
  │
  ├────► Value 2
  │
  ├────► Value 3
  │
  └────► Complete

So:

Promise     → one eventual settlement
Observable  → zero, one, or many values

3. Observable vs Promise Comparison

Feature Observable Promise
Provided by RxJS JavaScript
Number of values Zero, one, or many One settlement
Execution Usually lazy Eager once created
Consumer API subscribe() then() / await
Operators Many RxJS operators Limited built-in composition
Cancellation Can unsubscribe No general unsubscribe()
Streaming data Excellent Not designed for streams
Error handling error, catchError() catch, try/catch
Retry retry() Manual logic
Transformation map(), filter(), etc. then()
Angular HttpClient Native return type Can be converted if needed
Form valueChanges Excellent fit Poor fit
WebSocket/events Excellent fit Poor fit

4. Difference 1 — Single vs Multiple Values

Promise

A Promise settles only once.

const promise = new Promise<number>(resolve => {

  resolve(10);

  resolve(20);
  resolve(30);

});

promise.then(value => {
  console.log(value);
});

Output:

10

The later resolve() calls have no effect.

Conceptually:

Promise

        10
         │
         ▼
       resolve
         │
         ▼
        Done

Observable

An Observable can emit multiple values.

import { of } from 'rxjs';

const numbers$ = of(10, 20, 30);

numbers$.subscribe(value => {
  console.log(value);
});

Output:

10
20
30

Conceptually:

Observable

10 ─────►
20 ─────► Subscriber
30 ─────►

This is one of the most important differences.


5. Difference 2 — Eager vs Lazy

A Promise starts executing when it is created.

Promise

const promise = new Promise(resolve => {

  console.log('Promise started');

  resolve('Done');

});

console.log('Promise created');

Output:

Promise started
Promise created

Notice:

promise.then(...)

wasn't required to start the Promise executor.


Observable

Observables are generally lazy.

import { Observable } from 'rxjs';

const data$ = new Observable(subscriber => {

  console.log('Observable started');

  subscriber.next('Done');

});

console.log('Observable created');

At this point:

Observable created

The Observable producer hasn't run yet.

When:

data$.subscribe(value => {
  console.log(value);
});

then:

Observable started
Done

So:

Promise
   │
   └── Starts when created


Observable
   │
   └── Usually starts when subscribed

This is particularly important with Angular HttpClient.


6. Difference 3 — then() vs subscribe()

Promise uses:

promise.then(result => {
  console.log(result);
});

Or modern async/await:

const result = await promise;

Observable uses:

observable$.subscribe(result => {
  console.log(result);
});

More completely:

observable$.subscribe({

  next: value => {
    console.log(value);
  },

  error: error => {
    console.error(error);
  },

  complete: () => {
    console.log('Completed');
  }

});

Easy way to remember:

Promise      → then() / await

Observable   → subscribe()

7. Difference 4 — Cancellation

Observable subscriptions can be unsubscribed.

import { interval } from 'rxjs';

const timer$ = interval(1000);

const subscription = timer$.subscribe(value => {
  console.log(value);
});

Later:

subscription.unsubscribe();

The subscriber stops receiving values.

0
1
2
3

unsubscribe()

STOP

Promises do not have a general:

promise.unsubscribe();

API.

Cancellation for Promise-based operations requires separate mechanisms where the underlying API supports them, such as AbortController with fetch().


8. Difference 5 — RxJS Operators

This is a major advantage of Observables in Angular.

Suppose we have:

import { of } from 'rxjs';
import { filter, map } from 'rxjs/operators';

const numbers$ = of(1, 2, 3, 4, 5);

We can write:

numbers$
  .pipe(

    filter(number => number > 2),

    map(number => number * 10)

  )
  .subscribe(value => {
    console.log(value);
  });

Output:

30
40
50

Flow:

1  2  3  4  5
       │
       ▼
 filter(x > 2)
       │
       ▼
   3  4  5
       │
       ▼
 map(x * 10)
       │
       ▼
  30 40 50

RxJS provides many operators:

map()
filter()
tap()
switchMap()
mergeMap()
concatMap()
debounceTime()
distinctUntilChanged()
catchError()
retry()
take()
combineLatest()
forkJoin()

Promises have composition capabilities, but not this rich stream-processing operator model built in.


9. Difference 6 — Error Handling

Promise

Promise uses:

promise
  .then(result => {
    console.log(result);
  })
  .catch(error => {
    console.error(error);
  });

With async/await:

try {

  const result = await promise;

} catch (error) {

  console.error(error);

}

Observable

Observable can handle errors through the subscription:

observable$.subscribe({

  next: value => {
    console.log(value);
  },

  error: error => {
    console.error(error);
  }

});

Or using RxJS:

observable$
  .pipe(
    catchError(error => {
      // handle error
      return of([]);
    })
  );

This allows error handling to participate in the reactive pipeline.


10. Difference 7 — Retry

Suppose an HTTP request temporarily fails.

With RxJS:

this.http.get<Product[]>(url)
  .pipe(
    retry(2)
  )
  .subscribe(products => {

    console.log(products);

  });

Conceptually:

HTTP Request
     │
     ▼
   Failed
     │
     ▼
  Retry
     │
     ▼
   Failed
     │
     ▼
  Retry
     │
     ▼
  Success

With Promise-based code, retry behavior usually requires writing or using additional retry logic.


11. Difference 8 — Handling Streams

Suppose you have a search box.

The user types:

A
An
Ang
Angu
Angul
Angular

This is naturally a stream of values.

Angular Reactive Forms provides:

this.searchControl.valueChanges

which is an Observable.

You can then write:

this.searchControl.valueChanges
  .pipe(

    debounceTime(300),

    distinctUntilChanged(),

    switchMap(search =>
      this.productService.search(search)
    )

  )
  .subscribe(products => {

    this.products = products;

  });

This is a natural Observable use case.

User types
    │
    ▼
valueChanges
    │
    ▼
debounceTime
    │
    ▼
distinctUntilChanged
    │
    ▼
switchMap
    │
    ▼
Web API

A Promise is much less natural for representing the ongoing stream of search-box changes.


12. Angular HttpClient: Observable

Angular HttpClient returns Observables.

getProducts(): Observable<Product[]> {

  return this.http.get<Product[]>(
    '/api/products'
  );

}

Component:

this.productService
  .getProducts()
  .subscribe(products => {

    this.products = products;

  });

For a normal HTTP request, the Observable typically behaves like:

Subscribe
    │
    ▼
GET /api/products
    │
    ▼
Server Response
    │
    ▼
next(products)
    │
    ▼
complete()

Although Observables can emit many values, a normal Angular HTTP request usually emits one response and completes.


13. Can We Convert Observable to Promise?

Yes.

RxJS provides:

firstValueFrom()

and:

lastValueFrom()

For example:

import { firstValueFrom } from 'rxjs';

async loadProducts() {

  const products = await firstValueFrom(
    this.http.get<Product[]>('/api/products')
  );

  console.log(products);

}

This lets you use:

await

when Promise-style control flow is preferable.


14. Can We Convert Promise to Observable?

Yes.

Use RxJS from().

import { from } from 'rxjs';

const promise = fetch('/api/products');

const response$ = from(promise);

response$.subscribe(response => {

  console.log(response);

});

So conversion is possible in both directions.


15. When Should I Use Observable?

In Angular, prefer Observable when working naturally with reactive streams such as:

HttpClient
Reactive Forms valueChanges
Route parameters
WebSocket messages
User events
Timers
Search/autocomplete
Multiple dependent API calls
Shared application streams

Example:

searchControl.valueChanges
  .pipe(
    debounceTime(300),
    switchMap(search => this.search(search))
  );

Observable is especially powerful when data changes repeatedly over time.


16. When Is Promise Useful?

Promise is useful when you have a straightforward asynchronous operation with one eventual result and want simple async/await control flow.

For example:

async function loadData() {

  const response = await fetch('/api/products');

  const products = await response.json();

  console.log(products);
}

This style can be simple and readable for sequential one-result operations.


17. Very Important Interview Distinction

Don't say:

"Observable is asynchronous and Promise is synchronous."

That is incorrect.

Both are commonly used for asynchronous operations.

The important differences are:

Promise
 ├── one settlement
 ├── eager once created
 └── then() / await


Observable
 ├── zero, one, or many values
 ├── usually lazy
 ├── subscribe()
 ├── unsubscribe()
 └── powerful RxJS operators

18. Easy Real-World Analogy

Think about ordering food online.

Promise

You place one order.

Place Order
     │
     ▼
Waiting...
     │
     ▼
Food Delivered

One eventual outcome.


Observable

Think of tracking the order status:

Order placed
     ↓
Restaurant accepted
     ↓
Preparing
     ↓
Picked up
     ↓
Driver nearby
     ↓
Delivered

You're receiving multiple updates over time.

That is closer to an Observable stream.


Key Points

  • Promise is built into JavaScript.
  • Observable comes from RxJS.
  • Promise settles once with a value or error.
  • Observable can emit zero, one, or multiple values.
  • Promise uses then() / catch() or async/await.
  • Observable uses subscribe().
  • Promises are eager once created.
  • Observables are generally lazy.
  • Observable subscriptions can be unsubscribed.
  • RxJS provides powerful operators such as map, filter, switchMap, retry, and catchError.
  • Observables are well suited for continuous data streams.
  • Angular HttpClient returns Observables.
  • A normal HTTP Observable usually emits one response and completes.
  • Observables can be converted to Promises with firstValueFrom() when appropriate.

Interview Questions and Answers

1. What is the main difference between Observable and Promise?

Answer:
A Promise represents one eventual settlement, whereas an Observable can represent zero, one, or multiple values over time.


2. Which one is lazy?

Answer:
Observables are generally lazy and usually begin execution when subscribed. A Promise's executor starts when the Promise is created.


3. Can Promise return multiple values?

Answer:
No. A Promise settles once.

An Observable can emit multiple values:

of(10, 20, 30)

4. Can we cancel an Observable?

Answer:
An active Observable subscription can be stopped using:

subscription.unsubscribe();

Promises do not provide a general unsubscribe() mechanism.


5. Which one does Angular HttpClient return?

Answer:

Angular HttpClient returns:

Observable<T>

For example:

Observable<Product[]>

6. Can Observable be converted to Promise?

Answer:
Yes. For example:

const products = await firstValueFrom(
  this.http.get<Product[]>(url)
);

7. Which is better: Observable or Promise?

Answer:
Neither is universally better. Promise is simple for one-result asynchronous operations, while Observable is more suitable for reactive streams, cancellation/unsubscription, and complex asynchronous composition.


8. Why are Observables useful for search?

Answer:
Search input produces multiple values over time. Operators such as:

debounceTime()
distinctUntilChanged()
switchMap()

make it easy to control requests and switch to the latest search.


9. Does an HTTP Observable emit multiple responses?

Answer:
A normal Angular HttpClient request typically emits one response and then completes, even though the Observable abstraction itself supports multiple emissions.


10. Short Interview Answer

A Promise represents one future result and is eager once created, while an Observable represents a lazy stream that can emit zero, one, or multiple values and supports RxJS operators, composition, and unsubscription.

← Back to Articles