Angular RxJS Interview Questions
February 18, 2024
Q.1 : Explain RxJS Observable
.
Ans : Observable
pushes the data lazily from producer to consumer. It is a collection of multiple values. Data transfer takes place in push and pull mode. In pull mode, producer does not know when data is fetched by consumer because data is pulled by consumer. In push mode, consumer does not know when data will be pushed by producer. JavaScript functions work on pull mode. RxJS Observable
works on push mode. Observable
starts emitting data once it is subscribed. In Angular, Observable
can be subscribed using RxJS subscribe
or async
pipe.
In Angular
HttpClient
, HTTP methods return Observable
instance.
@Injectable({ providedIn: 'root' }) export class UserService { constructor(private http: HttpClient) { } findUserById(id: number): Observable<User> { return this.http.get<User>("/api/user", { params: new HttpParams().set('id', id) }); } }
Observable
from ‘rxjs’.
import { Observable, of, pipe, throwError } from 'rxjs';
Q.2 : How will you subscribe Observable
instance?
Ans : To subscribe Observable
, use RxJS subscribe
.
result$ = userService.findUserById(101); result$.subscribe({ next(v) { console.log("Value: ", v); }, error(e) { console.error('Error occurred: ' + e); }, complete() { console.log('Request completed.'); } });
async
pipe in HTML template.
{{result$ | async}}
Q.3 : What are RxJS operators?
Ans : RxJS operators are functions that are of two types.1. Pipeable Operators : Operators that can be used with
Observable.pipe()
such as map, switchMap, mergeMap, concatMap, takeUntil, retry, catchError, throwError etc. Pipeable operators takes an observable as input and returns another observable.
Import pipeable operator from 'rxjs/operators' .
import { map, switchMap, debounceTime, catchError } from 'rxjs/operators';
Import creation operator from 'rxjs' .
import { of, from, interval, fromEvent } from 'rxjs';
Q.4 : What is Observable.pipe()
and how to use it?
Ans : pipe
function takes pipeable operators as arguments. We can pass as many pipeable operators as we want. It passes the source observable to first operator and result of that operator is passed to another operator and so on.
of(101, 102).pipe( delay(1000), map(num => num * 2) ).subscribe(v => console.log(v));
Q.5 : What is difference between RxJS of
and from
?
Ans : of : Converts the specified arguments into an observable sequence.
of("a", "b", "c").subscribe(e => console.log(e));
from(["a", "b", "c"]).subscribe(e => console.log(e));
Q.6 : Explain RxJS map
operator.
Ans : map
applies the given function to each item emitted by source.
of(1, 2, 3, 4).pipe(map(e => e * e)) .subscribe(output => console.log(output));
Q.7 : Explain RxJS switchMap
operator.
Ans : switchMap
applies each element of source to a specified function that returns an observable called as inner observables. switchMap
always emits data only by latest inner observable. Suppose and inner observable is emitting data and mean while another inner observable is created then old one will stop and new one will emit data.
of(10, 20, 30).pipe( switchMap(e => of(2).pipe( delay(1000), map(v => v * e) ) )).subscribe(output => console.log(output));
switchMap
is useful in responding search results. If we keep on changing keywords in search, we will get response only for latest keyword.
Q.8 : Explain RxJS mergeMap
operator.
Ans : mergeMap
applies a given function to each source element and returns an observable as inner observable. At last all inner observables are merged.
of("a", "b", "c").pipe( mergeMap(e => of("@").pipe( map(v => v + e) ) )).subscribe(output => console.log(output));
Q.9 : Explain RxJS concatMap
operator.
Ans : concatMap
creates inner observable for every source items and waits to complete them. Later all output of inner observables are merged in their order they are processed.
of("Mohit", "Nilesh").pipe( concatMap(se => of("Shree").pipe( delay(2000), map(e => e + " " + se) )) ).subscribe(res => console.log(res));
(after 2 seconds) Shree Mohit (after 2 seconds) Shree Nilesh
Q.10 : Explain RxJS exhaustMap
operator.
Ans : exhaustMap
applies a given function to each item of source that returns an observable as inner observable and allows to emit only old inner observable if before its completion a new inner observable is created. exhaustMap
behaviour is opposite of switchMap
operator. Look into the below code snippet of exhaustMap
.
of(100, 200, 300).pipe( exhaustMap(e => of(2).pipe( delay(1000), map(v => v * e) ) )).subscribe(output => console.log(output));
Q.11 : How will you handle errors on observable using RxJS throwError
?
Ans : throwError
creates an error instance and emits it at once on subscribe.
of("A", "B", "C").pipe( map(se => { if (se === "B") { return throwError(() => "Error occurred for " + se); } return se; }) ).subscribe({ next(v) { console.log(v); }, error(e) { console.error(e); } });
Q.12 : How will you handle errors on observable using RxJS catchError
?
Ans : catchError
catches the error on observable and returns another observable by handling the error or can throw another error.
of("A", "B", "C").pipe( map(se => { if (se === "B") { throw new Error("Error occurred for " + se); } return se; }), catchError(err => { console.error(err.message); return of("Z"); }) ).subscribe({ next(v) { console.log(v); }, error(e) { console.error(e); } });
Q.13 : Explain RxJS retry
operator.
Ans : retry
operator resubscribes the source observable in case source observable calls error. We need to pass number of retry attempt to retry
operator such as retry(3)
and this will resubscribe source up to maximum 3 times. If no number is passed, retry()
will attempt for infinite time if error call continues on source observable.
of("A").pipe( mergeMap(data => { console.log(data); if (data === "A") { return throwError(() => 'Error Occurred.'); } return of(data); }), retry(3) ).subscribe({ next(v) { console.log(v); }, error(e) { console.error(e); } });
Q.14 : Explain RxJS filter
operator.
Ans : filter
operator filters the emitted values by source. It allows to emit only those values that satisfies the specified predicate.
of(1, 2, 3, 4, 5, 6, 7).pipe( filter(num => num % 2 === 0) ).subscribe(v => console.log(v));
Q.15 : Explain RxJS tap
operator.
Ans : tap
is used to perform side-effects on source items such as logging. tap
returns the same observable obtained from source.
of(1, 2, 3, 4, 5, 6, 7).pipe( tap(s => console.log("Before Filter: " + s)), filter(num => num % 2 === 1), tap(s => console.log("After Filter: " + s)) ).subscribe(v => console.log(v));
Q.16 : Explain RxJS takeUntil
operator.
Ans : RxJS takeUntil
operator allows source observable to emit until another observable specified to it starts to emit.
interval(500).pipe( takeUntil(timer(2000)) ).subscribe(e => console.log(e));
interval(500)
is emitting data after every 500 ms. Once timer(2000)
will emit after 2000 ms. For these two 2000 ms, source observable interval(500)
will emit data as 0 1 2 3 4.
Q.17 : Explain RxJS takeWhile
operator.
Ans : takeWhile
works same as takeUntil
but the difference is takeWhile
accepts an observable whereas takeWhile
accepts a predicate. takeWhile
allows source observable to emit until predicate returns false.
of(1, 2, 3, 4).pipe( takeWhile((v, i) => v < 3) ).subscribe(e => console.log(e));
v and i is value and index of source observable emission. Each item will be checked by predicate, if predicate returns true then that item is passed. Once predicate returns false source observable stops emitting and completes itself.
Q.18 : Explain RxJS takeLast
operator.
Ans : takeLast
emits last N number of items emitted by source after completion. Here N is the number specified to it.
of("A", "B", "C", "D").pipe( takeLast(2) ).subscribe(item => console.log(item));
takeLast(2)
will emit last two items after source has emitted all its items.
Q.19 : Explain RxJS take
operator.
Ans : take
emits first N number of items from source observable and then completes.
of("A", "B", "C", "D").pipe( take(2) ).subscribe(item => console.log(item));
take(2)
allows source to emit first two numbers and then completes.
Q.20 : Explain RxJS max
and min
operators.
Ans : max
and min
emit item with maximum and minimum values respectively for the given comparer function. If comparer is not specified, they emit max and min with natural order. In case of number they emit max or min number and in case of alphabet they emit max or min in alphabetic order.
Find an example with comparer function.
Using
max
:
const itemWeights = [ { name: "ABC", weight: 10 }, { name: "PQR", weight: 15 }, { name: "XYZ", weight: 5 } ]; from(itemWeights).pipe( max((item1, item2) => item1.weight < item2.weight ? -1 : 1) ).subscribe(item => console.log(item));
{ name: "PQR", weight: 15 }
min
:
from(itemWeights).pipe( min((item1, item2) => item1.weight < item2.weight ? -1 : 1) ).subscribe(item => console.log(item));
{ name: "XYZ", weight: 5 }
Q.21 : Explain RxJS count
operator.
Ans : count
operator counts the number of items emitted by source and count is emitted once source observable emission is complete.
of("P", "Q", "R", "S").pipe( count() ).subscribe(num => console.log(num));
Q.22 : Explain RxJS delayWhen
operator.
Ans : delayWhen
delays the source observable emission by the given delay duration selector observable that emits a time to delay.
of("A", "B").pipe( delayWhen((item, index) => { console.log(item + "-" + index); return timer(2000); }) ).subscribe(e => console.log(e));
delayWhen
has two arguments, first is item and second is index of that item. We can use these arguments to set delay time for each items separately.
Q.23 : Explain RxJS delay
operator.
Ans : delay
delay source emission by the given due time as number or date. We can pass time in milliseconds and once that time is passed then emission starts. If we pass date then once that date is passed then emission starts.
of(101, 102).pipe( delay(3000) ).subscribe(e => console.log(e));
The difference in
delay
and delayWhen
is in their arguments. The delay
accepts a number or date to delay whereas delayWhen
accepts a function that returns an observable that emits time to delay.
Q.24 : Explain RxJS debounce
operator.
Ans : debounce
emits only latest item emitted by source after the delay time emitted by another observable.
of ("X", "Y", "Z").pipe( debounce(v=> interval(1000)) ).subscribe(v => console.log(v));
interval(1000)
will emit after one second. Each item of source will be discarded by debounce
if duration time is not over. In our case, source will emit with no delay and hence all item will reach to debounce
one-by-one and every will be discarded if new item reaches. In this way Z will reach at last and hence after 1 second, Z will be emitted.
Q.25 : Explain RxJS debounceTime
operator.
Ans : debounceTime
emits a source item only after the specified time has passed without another source emisssion.
of ("A", "B", "C").pipe( debounceTime(1000) ).subscribe(v => console.log(v));
Q.26 : Explain RxJS combineLatestWith
operator.
Ans : combineLatestWith
combines latest values from source and all inner observables into array.
TS
result$: any; ngOnInit() { this.result$ = of("X", "Y").pipe( combineLatestWith(of("101")), combineLatestWith(of("102")) ); }
{{result$ | async}}
Q.27 : Explain RxJS fromEvent
operator.
Ans : fromEvent
creates an observable that emits specified event by specified target.
Suppose we want to emit click event by clicking on a button, we will write code in Angular as below.
@Component({ selector: 'click-app', standalone: true, template: '<button #click>Click Me!</button>' }) export class ClickComponent implements AfterViewInit { @ViewChild('click') clickMe!: ElementRef; ngAfterViewInit() { const clicks$ = fromEvent(this.clickMe.nativeElement, 'click'); clicks$.subscribe(click => console.log(click)); } }