Promise vs Observable in Angular with (.subscribe, .then , .pipe).

Suravi Sasadara
eVision
Published in
11 min readJul 19, 2020

######################################################################################## Advance best Explanation definitely Read below ############################################################################### #########

we will discuss the differences between promises and observables. In Angular 2, to work with asynchronous data we can use either Promises or Observables. In our previous videos in this series, we discussed using both Observables and Promises. There are several differences between Promises and Observables. In this video let’s discuss these differences.

As a quick summary the differences are shown in the table below
PromiseObservableEmits a single valueEmits multiple values over a period of timeNot LazyLazy. An Observable is not called until we subscribe to the ObservableCannot be cancelledCan be cancelled using the unsubscribe() methodObservable provides operators like map, forEach, filter, reduce, retry, retryWhen etc.

A Promise emits a single value where as an Observable emits multiple values over a period of time. You can think of an Observable like a stream which emits multiple items over a period of time and the same callback function is called for each item emitted. So with an Observable we can use the same API to handle asynchronous data whether that data is emitted as a single value or multiple values over a period of time.

A Promise is not lazy where as an Observable is Lazy. Let’s prove this with an example. Consider this method getEmployeeByCode() in employee.service.ts. Notice this method returns an Observable.

getEmployeeByCode(empCode: string): Observable<IEmployee> {

return this._http.get(“http://localhost:31324/api/employees/" + empCode)

.map((response: Response) => <IEmployee>response.json())

.catch(this.handleError);

}

Here is the consumer code of the above service. In our example, this code is in employee.component.ts in ngOnInit() method. Notice we are subscribing to the Observable using the subscribe() method. An Observable is lazy because, it is not called and hence will not return any data until we subscribe using the subscribe() method. At the moment we are using the subscribe() method. So the service method should be called and we should receive data.

ngOnInit() {

let empCode: string = this._activatedRoute.snapshot.params[‘code’];

this._employeeService.getEmployeeByCode(empCode)

.subscribe((employeeData) => {

if (employeeData == null) {

this.statusMessage =

‘Employee with the specified Employee Code does not exist’;

}

else {

this.employee = employeeData;

}

},

(error) => {

this.statusMessage =

‘Problem with the service. Please try again after sometime’;

console.error(error);

});

}

To prove this
1. Launch Browser developer tools by pressing F12 while you are on the browser.
2. Navigate to /src/employees/emp101
3. Click on the Network Tab
4. In the Filter textbox, type “api/employees”
5. At this point, you should only see the request issued to EmployeeService in the table below
6 Hover the mouse over “emp101” under “Name” column in the table

7. Notice the request to employee service (/api/employees/emp101) is issued

Instead of hovering over the request, if you click on it, you can see the response data as shown below. Make sure you select the “preview” tab.

Now in employee.component.ts file, comment the subscribe() method code block as shown below. Notice we are still calling the getEmployeeByCode() method of the EmployeeService. Since we have not subscribed to the Observable, a call to the EmployeeService will not be issued over the network.

ngOnInit() {

let empCode: string = this._activatedRoute.snapshot.params[‘code’];

this._employeeService.getEmployeeByCode(empCode)

//.subscribe((employeeData) => {

// if (employeeData == null) {

// this.statusMessage =

// ‘Employee with the specified Employee Code does not exist’;

// }

// else {

// this.employee = employeeData;

// }

//},

//(error) => {

// this.statusMessage =

// ‘Problem with the service. Please try again after sometime’;

// console.error(error);

//});

}

With the above change in place, reload the web page. Notice no request is issued over the network to the EmployeeService. You can confirm this by looking at the network tab in the browser developer tools. So this proves that an Observable is lazy and won’t be called unless we subscribe using the subscribe() method.

Now, let’s prove that a Promise is NOT Lazy. I have modified the code in employee.service.ts to return a Promise instead of an Observable. The modified code is shown below.

getEmployeeByCode(empCode: string): Promise<IEmployee> {

return this._http.get(“http://localhost:31324/api/employees/" + empCode)

.map((response: Response) => <IEmployee>response.json())

.toPromise()

.catch(this.handlePromiseError);

}

Here is the consumer code of the above service. In our example, this code is in employee.component.ts in ngOnInit() method.

ngOnInit() {

let empCode: string = this._activatedRoute.snapshot.params[‘code’];

this._employeeService.getEmployeeByCode(empCode)

.then((employeeData) => {

if (employeeData == null) {

this.statusMessage =

‘Employee with the specified Employee Code does not exist’;

}

else {

this.employee = employeeData;

}

},

(error) => {

this.statusMessage =

‘Problem with the service. Please try again after sometime’;

console.error(error);

});

}

Because a promise is eager(not lazy), calling employeeService.getEmployeeByCode(empCode) will immediately fire off a request across the network to the EmployeeService. We can confirm this by looking at the network tab in the browser tools.

Now you may be thinking, then() method in this case is similar to subscribe() method. If we comment then() method code block, will the service still be called. The answer is YES. Since a Promise is NOT LAZY, Irrespective of whether you have then() method or not, calling employeeService.getEmployeeByCode(empCode) will immediately fire off a request across the network to the EmployeeService. You can prove this by commenting then() method code block and reissuing the request.

################################################################################################################################################
http://csharp-video-tutorials.blogspot.com/2017/09/angular-promises-vs-observables.htmlhttps://medium.com/@mpodlasin/promises-vs-observables-4c123c51fe13
Examples =: https://github.com/jpssasadara/web_Socket_Epic_POS_Final/tree/master/Fhttps://github.com/jpssasadara/Ex_Hub/tree/master/1.5%20Ex_hub
##################################################################################################################################################

Angular Observable pipe

his page will walk through Angular Observable pipe example. RxJS pipe is used to combine functional operators into a chain. pipe is an instance method of Observable as well as a standalone RxJS function. pipe can be used as Observable.pipe or we can use standalone pipe to combine functional operators. The declaration of pipe is as following.

public pipe(operations: ...*): Observable

pipe accepts operators as arguments such as filter, map, mergeScan etc with comma separated and execute them in a sequence they are passed in as arguments and finally returns Observable instance. To get the result we need to subscribe it. Now let us discuss the complete example.

Contents

echnologies Used

Find the technologies being used in our example.
1. Angular 7.0.0
2. Angular CLI 7.0.3
3. TypeScript 3.1.1
4. Node.js 10.3.0
5. NPM 6.1.0
6. RxJS 6.3.3
7. In-Memory Web API 0.6.1

Import RxJS 6 Observable and Operators

In RxJS 6, Observable and operators are imported as following.
1. Import Observable and of from rxjs.

import { Observable, of } from 'rxjs';

pipe will be accessed using Observable.pipe. To use standalone pipe, we can also import it.

import { pipe } from 'rxjs';

2. Operators are imported from rxjs/operators. Find some operators to import it.

import { mergeMap, switchMap, retry, 
map, catchError, filter, scan } from 'rxjs/operators';

Observable.pipe with filter, map and scan

Here we will use Observable.pipe to combine functions. Suppose we have a service method to get numbers.

getNumbers(): Observable<number> {
return of(1, 2, 3, 4, 5, 6, 7);
}

1. Find the code to use pipe with filter operator.

calculateNumbers() {
this.bookService.getNumbers().pipe(
filter(n => n % 2 === 1)
)
.subscribe(result => console.log(result));
}

Output will be 1,3,5,7.
We can observe that we are passing filter operator within pipe. Final result of pipe after executing filter, will be instance of Observable. To get the result we need to subscribe it.
2. Find the code to use pipe with filter and map operators.

calculateNumbers() {
this.bookService.getNumbers().pipe(
filter(n => n % 2 === 1),
map(n => n + 10)
)
.subscribe(result => console.log(result));
}

Output will be 11, 13, 15, 17.
In the above code we are passing filter and map operators in pipe function as arguments. filter and map will execute in the order they are passed in. On the Observable instance returned by getNumbers(), filter operator will be executed and on the Observable instance returned by filter, map operator will be executed and the final result returned by pipe will be the result returned by last operator i.e. map in above code.
3. Find the code to use pipe with filter, map and scan operators.

calculateNumbers() {
this.bookService.getNumbers().pipe(
filter(n => n % 2 === 1),
map(n => n + 10),
scan((sum, n) => sum + n)
)
.subscribe(result => console.log(result));
}

The output will be 11, 24, 39, 56.
In the above code we are using three operators with pipe i.e. filter, map and scan. They will execute in the order they are passed in as arguments. In the above code, first filter will execute then map and then scan. Final output returned by pipe will be Observable returned by scan.

Using Standalone pipe

To use pipe standalone, we need to import it as following.

import { pipe } from 'rxjs';

Now find the code snippet to use it.

calculateNumbers() {
//Create a function to accept Observable instance
const calculationFun = pipe(
filter((n: number) => n % 2 === 1),
map((n: number) => n + 10),
scan((sum, n) => sum + n)
);
//Instantiate response Observable
const calculation$ = calculationFun(this.bookService.getNumbers());
//Subscribe the Observable instance
calculation$.subscribe(result => console.log(result));
}

Output will be 11, 24, 39, 56.

Error Handling: pipe with retry and catchError

retry operator reties to access URL for the given number of time before failing. catchError has been introduced in RxJS 6. catchError is used in place of catch. Find the code to use pipe with retry and catchError operator.

getBooks() {
this.allBooks$ = this.bookService.getBooksFromStore().pipe(
retry(3),
map(books => {
if (books.length < 5) {
throw new Error('Not enough books');
}
return books;
}),
catchError(err => {
console.error(err);
return of([]);
})
);
}

We can also subscribe the Observable instance.

getBooks() {
this.bookService.getBooksFromStore().pipe(
retry(3),
map(books => {
if (books.length < 5) {
throw new Error('Not enough books');
}
return books;
}),
catchError(err => {
console.error(err);
return of([]);
})
)
.subscribe(books => this.allBooks = books);
}

pipe with mergeMap

Find the code snippet to use pipe with mergeMap operator.

getAllFavBooks() {
this.bookService.getFavBookFromStore(101).pipe(
mergeMap(book => {
let category = book.category;
return this.bookService.getBooksByCategoryFromStore(category);
})
).subscribe(books => {
this.allFavBooks = books;
});
}

pipe with switchMap

Find the code snippet to use pipe with switchMap operator.

searchSimilarBooks(id: number) {
this.bookService.getFavBookFromStore(id).pipe(
switchMap(book => {
let category = book.category;
return this.bookService.getBooksByCategoryFromStore(category);
}),
catchError(err => of([]))
)
.subscribe(books => {
this.similarBooks = books;
console.log(books);
});
}

Complete Example

Here we will provide complete demo to use pipe.
book.component.ts

import { Component, OnInit } from '@angular/core';
import { Observable, of, pipe } from 'rxjs';
import { mergeMap, switchMap, retry,
map, catchError, filter, scan } from 'rxjs/operators';
import { BookService } from './book.service';
import { Book } from './book';
@Component({
selector: 'app-book',
templateUrl: './book.component.html'
})
export class BookComponent implements OnInit {
softBooks: Book[];
allFavBooks: Book[];
similarBooks: Book[];
constructor(private bookService: BookService) { }

ngOnInit() {
this.calculateNumbers1();
this.calculateNumbers2();
this.getSoftBooks();
this.getAllFavBooks();
}
//Using Observable.pipe with filter, map and scan
calculateNumbers1() {
this.bookService.getNumbers().pipe(
filter(n => n % 2 === 1),
map(n => n + 10),
scan((sum, n) => sum + n)
)
.subscribe(result => console.log(result));
}
//Using Standalone pipe
calculateNumbers2() {
//Create a function to accept Observable instance
const calculationFun = pipe(
filter((n: number) => n % 2 === 1),
map((n: number) => n + 10),
scan((sum, n) => sum + n)
);
//Instantiate response Observable
const calculation$ = calculationFun(this.bookService.getNumbers());
//Subscribe the Observable instance
calculation$.subscribe(result => console.log(result));
}

//Using retry() and catchError operator
getSoftBooks() {
this.bookService.getBooksFromStore().pipe(
retry(3),
map(books => {
if (books.length < 5) { //It will throw error in console
throw new Error('Not enough books');
}
return books;
}),
catchError(err => {
console.error(err);
return of([]);
})
)
.subscribe(books => this.softBooks = books);
}
//Using mergeMap
getAllFavBooks() {
this.bookService.getFavBookFromStore(101).pipe(
mergeMap(book => {
let category = book.category;
return this.bookService.getBooksByCategoryFromStore(category);
})
).subscribe(books => {
this.allFavBooks = books;
});
}

//Using switchMap
searchSimilarBooks(id: number) {
this.bookService.getFavBookFromStore(id).pipe(
switchMap(book => {
let category = book.category;
return this.bookService.getBooksByCategoryFromStore(category);
}),
catchError(err => of([]))
)
.subscribe(books => {
this.similarBooks = books;
console.log(books);
});
}
}

book.component.html

<b>All Favorite Books</b><br/>
<ul>
<li *ngFor="let book of allFavBooks" >
Id: {{book.id}}, Name: {{book.name}}, Category: {{book.category}}
</li>
</ul>
<b>Search Similar Books</b><br/><br/>

ID: <input [(ngModel)]="bookId" (input)="searchSimilarBooks(bookId)">
<ul>
<li *ngFor="let book of similarBooks" >
Id: {{book.id}}, Name: {{book.name}}, Category: {{book.category}}
</li>
</ul>

book.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { Book } from './book';
@Injectable({
providedIn: 'root'
})
export class BookService {
bookUrl = "/api/books";
constructor(private http: HttpClient) { }

getNumbers(): Observable<number> {
return of(1, 2, 3, 4, 5, 6, 7);
}
getBooksFromStore(): Observable<Book[]> {
return this.http.get<Book[]>(this.bookUrl);
}
getFavBookFromStore(id: number): Observable<Book> {
return this.http.get<Book>(this.bookUrl + "/" + id);
}
getBooksByCategoryFromStore(category: string): Observable<Book[]> {
return this.http.get<Book[]>(this.bookUrl + "?category=" + category);
}
}

book.ts

export interface Book {
id: number;
name: string;
category: string;
}

app.component.ts

import { Component } from '@angular/core';@Component({
selector: 'app-root',
template: `
<app-book></app-book>
`
})
export class AppComponent {
}

test-data.ts

import { InMemoryDbService } from 'angular-in-memory-web-api';export class TestData implements InMemoryDbService {
createDb() {
let bookDetails = [
{ id: 101, name: 'Angular by Krishna', category: 'Angular' },
{ id: 102, name: 'Core Java by Vishnu', category: 'Java' },
{ id: 103, name: 'NgRx by Rama', category: 'Angular' }
];
return { books: bookDetails };
}
}

app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { AppComponent } from './app.component';
import { BookComponent } from './book.component';
import { BookService } from './book.service';
//For InMemory testing
import { InMemoryWebApiModule } from 'angular-in-memory-web-api';
import { TestData } from './test-data';
@NgModule({
imports: [
BrowserModule,
HttpClientModule,
FormsModule,
InMemoryWebApiModule.forRoot(TestData)
],
declarations: [
AppComponent,
BookComponent
],
providers: [
],
bootstrap: [
AppComponent
]
})
export class AppModule { }

Run Application

To run the application, find the steps.
1. Download source code using download link given below on this page.
2. Use downloaded src in your Angular CLI application. To install Angular CLI, find the link.
3. Install angular-in-memory-web-api@0.6.1
4. Run ng serve using command prompt.
5. Access the URL http://localhost:4200
Find the print screen of the output.

References

RxJS Observable
Angular: The RxJS library
Pipeable Operators

Download Source Code

angular-observable-pipe.zip
Creating pipe ==> https://alligator.io/angular/custom-pipes-angular/

--

--