Observables vs Promises
a promise performs a fetch and we can use then method to wait for the response, in recent years we have available the keywords async and await to process the response.
Observable uses a different approeach, in this case we work witha data stream and then we subscribe to it. As soon as we get new data comming from the stream our subscription is going to be notify and we will have the incoming data available.
By definition, promises are called one time and are resolved once.
On the other hands, observables allow us to stream data, send data multiple times.
Observables allow us to cancel them and we can manipulate the data stream using pipes.
Angular uses observable in its core to process http requests, routing, changes in the window, etc.
RXjs
RxJS is a library that allows us to handle reactivity all over the app via subscription publisher pattern.
One common class that we can use to implement this functionality is BehaviorSubject class
import { Injectable } from "@angular/core";
import { BehaviorSubject } from "rxjs";
import { Product } from "./../../core/models/product.model";
@Injectable({
providedIn: "root",
})
export class CartService {
// This value is the dynamic value, we want to react to its changes
private products: Product[] = [];
// This BehaviorSubject instance defines the way reactivity is going to be hanle
private cart = new BehaviorSubject<Product[]>([]);
// Observable object consume by subscriptors
cart$ = this.cart.asObservable();
constructor() {}
addToCart(product: Product) {
// setting new value
this.products = [...this.products, product];
// sending new value to suscriptors
this.cart.next(this.products);
}
}
addToCart method can be used from any component injecting the service.
Now how can we subscribe to this reactive class?
import { Component, OnInit } from '@angular/core';
import { map } from 'rxjs/operators';
import { CartService } from '@core/services/cart.service';
import { Observable } from 'rxjs';
@Component({
selector: 'app-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.scss'],
})
export class HeaderComponent implements OnInit {
total$: Observable<number>;
constructor(private cartService: CartService) {
this.total$ = this.cartService.cart$.pipe(
map((products) => products.length;)
);
}
ngOnInit(): void {}
}
Using switchMap to reduce the number of subscriptions
Code we want to optimize
product: Product;
ngOnInit(): void {
this.route.params.subscribe((params: Params) => {
const id = params.id;
this.fetchProduct(id);
});
}
fetchProduct(id: string) {
this.productsService.getProduct(id).subscribe((product) => {
this.product = product;
});
}
Optimized code
product$: Observable<Product>;
constructor(
private route: ActivatedRoute,
private productsService: ProductsService
) {}
ngOnInit(): void {
this.product$ = this.route.params.pipe(
switchMap(
(params: Params) => this.productsService.getProduct(params.id)
)
);
}
Consuming the observable in a component
<div *ngIf="(product$ | async) as product">
Handling errors in subscriptions
subscribe(successFunction, (error) => {
handlingError();
});
We can use the function handlingError to show logs or event store the event or send it to sentry, etc.
Top comments (0)