├── assets └── Untitled.png └── README.md /assets/Untitled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yugsolanki/learn-rxjs/HEAD/assets/Untitled.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RXJS 2 | 3 | # Introduction 4 | 5 | Observables are a fundamental concept in RxJS. They represent a stream of values or events over time. In other words, an observable is a sequence of data that can be observed and reacted to. It can emit multiple values asynchronously, including values, errors, and completion signals. 6 | 7 | Observables are used to handle asynchronous and event-based programming by providing a way to manage and manipulate data streams. They can be created from various sources such as user interactions, network requests, timers, or existing data structures. 8 | 9 | Observables have three main characteristics: 10 | 11 | 1. Data Producer: Observables are responsible for producing and emitting values or events over time. 12 | 2. Lazy: Observables are lazy by nature. They don't start emitting values until there is a subscriber. 13 | 3. Cancellation: Observables can be canceled or unsubscribed from, allowing you to control the lifecycle of the data stream. 14 | 15 | Observables can be transformed, combined, and manipulated using various operators provided by RxJS, enabling powerful data manipulation and composition. They also support a wide range of features like filtering, mapping, merging, combining, and more. 16 | 17 | To summarize, observables are the core building blocks of reactive programming in RxJS, providing a way to handle asynchronous and event-based data streams. 18 | 19 | # How to create observables (Basics) 20 | 21 | RxJS provides several methods to create observables from different sources. Here are some common ways to create observables: 22 | 23 | 1. Creating an Observable from an Array: 24 | 25 | ```jsx 26 | import { of } from 'rxjs'; 27 | 28 | const array = [1, 2, 3, 4, 5]; 29 | const arrayObservable = of(...array); 30 | arrayObservable.subscribe(value => console.log(value)); 31 | 32 | ``` 33 | 34 | 2. Creating an Observable from an Event: 35 | 36 | ```jsx 37 | import { fromEvent } from 'rxjs'; 38 | 39 | const button = document.getElementById('myButton'); 40 | const buttonClickObservable = fromEvent(button, 'click'); 41 | buttonClickObservable.subscribe(event => console.log(event)); 42 | 43 | ``` 44 | 45 | 3. Creating an Observable from a Promise: 46 | 47 | ```jsx 48 | import { from } from 'rxjs'; 49 | 50 | const promise = new Promise(resolve => resolve('Hello, RxJS!')); 51 | const promiseObservable = from(promise); 52 | promiseObservable.subscribe(value => console.log(value)); 53 | 54 | ``` 55 | 56 | 4. Creating a Custom Observable: 57 | You can create a custom observable using the `Observable` constructor and providing a subscribe function. The subscribe function defines how the observable will emit values or events. 58 | 59 | ```jsx 60 | import { Observable } from 'rxjs'; 61 | 62 | const customObservable = new Observable(observer => { 63 | observer.next(1); 64 | observer.next(2); 65 | observer.next(3); 66 | observer.complete(); 67 | }); 68 | 69 | customObservable.subscribe(value => console.log(value)); 70 | 71 | ``` 72 | 73 | 74 | These are just a few examples of how to create observables from different sources. RxJS offers many more creation operators and methods to create observables from timers, AJAX requests, WebSocket connections, and more. 75 | 76 | # All different ways to create observables 77 | 78 | Certainly! Here are some of the different ways to create observables in RxJS: 79 | 80 | 1. Creation Operators: 81 | - `of`: Creates an observable that emits a sequence of values. 82 | - `from`: Converts an array, iterable, promise, or other observable-like objects into an observable. 83 | - `interval`: Emits incremental numbers at a specified interval. 84 | - `timer`: Emits a value after a specified delay or at regular intervals. 85 | - `fromEvent`: Creates an observable from DOM events or Node.js EventEmitter. 86 | - `ajax`: Creates an observable from AJAX requests. 87 | - `create`: Creates a custom observable using the `Observable` constructor. 88 | 2. Subject: 89 | - `Subject`: Represents a special type of observable that can act as both an observer and an observable. It allows multicasting to multiple subscribers. 90 | 3. Combining Observables: 91 | - `combineLatest`: Combines multiple observables into a single observable that emits an array of the latest values from each source observable. 92 | - `concat`: Concatenates multiple observables sequentially, emitting values in the order they are provided. 93 | - `merge`: Merges multiple observables into a single observable, emitting values as they are emitted from any source observable. 94 | - `zip`: Combines multiple observables into a single observable by emitting values pairwise based on their order. 95 | 4. Transformation Operators: 96 | - `map`: Applies a projection function to each value emitted by the source observable. 97 | - `filter`: Filters values emitted by the source observable based on a condition. 98 | - `reduce`: Accumulates values emitted by the source observable and emits the accumulated result. 99 | 5. Error Handling: 100 | - `catchError`: Catches errors thrown by the source observable and replaces them with a new observable or value. 101 | - `retry`: Resubscribes to the source observable when it encounters an error, allowing for retry attempts. 102 | 103 | These are just a few examples of the different ways to create and manipulate observables in RxJS. The library provides a rich set of operators and methods to handle various use cases and scenarios. 104 | 105 | --- 106 | 107 | ## 1. Creation Operators 108 | 109 | Certainly! Here are examples of using the creation operators `of`, `from`, `interval`, `timer`, `fromEvent`, `ajax`, and `create`: 110 | 111 | 1. `of`: 112 | 113 | ```jsx 114 | import { of } from 'rxjs'; 115 | 116 | const values = of(1, 2, 3, 4, 5); 117 | values.subscribe(value => console.log(value)); 118 | // Output: 1, 2, 3, 4, 5 119 | ``` 120 | 121 | 2. `from`: 122 | 123 | ```jsx 124 | import { from } from 'rxjs'; 125 | 126 | const array = [1, 2, 3, 4, 5]; 127 | const arrayObservable = from(array); 128 | arrayObservable.subscribe(value => console.log(value)); 129 | // Output: 1, 2, 3, 4, 5 130 | 131 | const promise = new Promise(resolve => resolve('Hello, RxJS!')); 132 | const promiseObservable = from(promise); 133 | promiseObservable.subscribe(value => console.log(value)); 134 | // Output: Hello, RxJS! 135 | ``` 136 | 137 | 3. `interval`: 138 | 139 | ```jsx 140 | import { interval } from 'rxjs'; 141 | 142 | const source = interval(1000); 143 | source.subscribe(value => console.log(value)); 144 | // Output: 0, 1, 2, 3, 4, ... 145 | ``` 146 | 147 | 4. `timer`: 148 | 149 | ```jsx 150 | import { timer } from 'rxjs'; 151 | 152 | const source = timer(2000, 1000); // Delay of 2 seconds, then emit every 1 second 153 | source.subscribe(value => console.log(value)); 154 | // Output: 0, 1, 2, 3, 4, ... 155 | ``` 156 | 157 | 5. `fromEvent`: 158 | 159 | ```jsx 160 | import { fromEvent } from 'rxjs'; 161 | 162 | const button = document.getElementById('myButton'); 163 | const buttonClickObservable = fromEvent(button, 'click'); 164 | buttonClickObservable.subscribe(event => console.log(event)); 165 | // Output: MouseEvent object when the button is clicked 166 | 167 | ``` 168 | 169 | 6. `ajax`: 170 | 171 | ```jsx 172 | import { ajax } from 'rxjs/ajax'; 173 | 174 | const url = ''; 175 | const ajaxObservable = ajax.getJSON(url); 176 | ajaxObservable.subscribe(data => console.log(data)); 177 | // Output: Data received from the API endpoint 178 | 179 | ``` 180 | 181 | 7. `create`: 182 | 183 | ```jsx 184 | import { Observable } from 'rxjs'; 185 | 186 | const customObservable = new Observable(observer => { 187 | observer.next(1); 188 | observer.next(2); 189 | observer.next(3); 190 | observer.complete(); 191 | }); 192 | 193 | customObservable.subscribe(value => console.log(value)); 194 | // Output: 1, 2, 3 195 | ``` 196 | 197 | 198 | These examples demonstrate how to create observables using various creation operators in RxJS. 199 | 200 | --- 201 | 202 | ## 2. Using ‘Subject’ 203 | 204 | Certainly! Here's an example of using a `Subject` in RxJS: 205 | 206 | ```jsx 207 | import { Subject } from 'rxjs'; 208 | 209 | // Create a new Subject 210 | const subject = new Subject(); 211 | 212 | // Subscribe to the Subject 213 | subject.subscribe(value => console.log('Subscriber A:', value)); 214 | 215 | // Emit values to the Subject 216 | subject.next(1); // Output: Subscriber A: 1 217 | subject.next(2); // Output: Subscriber A: 2 218 | 219 | // Subscribe another subscriber to the Subject 220 | subject.subscribe(value => console.log('Subscriber B:', value)); 221 | 222 | // Emit values to the Subject again 223 | subject.next(3); 224 | subject.next(4); 225 | // Output: Subscriber A: 3 226 | // Output: Subscriber B: 3 227 | // Output: Subscriber A: 4 228 | // Output: Subscriber B: 4 229 | 230 | // Complete the Subject 231 | subject.complete(); 232 | 233 | // Subscriber C won't receive any values after completion 234 | subject.subscribe(value => console.log('Subscriber C:', value)); 235 | // Output: Subscriber C: 1 (if it subscribed before the completion) 236 | 237 | ``` 238 | 239 | In this example, we create a `Subject` called `subject`. We then subscribe two subscribers, "Subscriber A" and "Subscriber B," to the `subject`. When we call `subject.next(value)`, it emits the value to all the subscribed subscribers. So both "Subscriber A" and "Subscriber B" receive the emitted values. 240 | 241 | Afterward, we subscribe "Subscriber C" to the `subject`, but since the `subject` has already completed, it won't receive any further values. 242 | 243 | `Subject` is particularly useful when you want to multicast values to multiple subscribers, allowing them to receive the same set of emitted values. 244 | 245 | 253 | 254 | Certainly! Here's an explanation of `BehaviorSubject`, `ReplaySubject`, and `AsyncSubject`, along with examples for each: 255 | 256 | 1. **BehaviorSubject**: 257 | - `BehaviorSubject` is a variation of `Subject` that holds a current value and emits it to new subscribers. It remembers the latest value and immediately provides it to any new subscriber. 258 | - When a new subscriber subscribes to a `BehaviorSubject`, it receives the last emitted value or the initial value if no values have been emitted yet. 259 | - `BehaviorSubject` also emits new values to its subscribers, just like a regular `Subject`. 260 | - Example: 261 | 262 | ```jsx 263 | import { BehaviorSubject } from 'rxjs'; 264 | 265 | // Create a BehaviorSubject with an initial value of 0 266 | const subject = new BehaviorSubject(0); 267 | 268 | // Subscribe to the BehaviorSubject 269 | subject.subscribe(value => console.log('Subscriber A:', value)); 270 | 271 | // Output: Subscriber A: 0 272 | 273 | // Emit a new value 274 | subject.next(1); 275 | 276 | // Output: Subscriber A: 1 277 | 278 | // Subscribe a new subscriber 279 | subject.subscribe(value => console.log('Subscriber B:', value)); 280 | 281 | // Output: Subscriber B: 1 (receives the latest value immediately) 282 | 283 | ``` 284 | 285 | 2. **ReplaySubject**: 286 | - `ReplaySubject` is a variation of `Subject` that remembers a specified number of previously emitted values and replays them to new subscribers. 287 | - When a new subscriber subscribes to a `ReplaySubject`, it receives a defined number of past values emitted by the `ReplaySubject`. 288 | - The number of past values to be replayed is specified when creating the `ReplaySubject`. 289 | - Example: 290 | 291 | ```jsx 292 | import { ReplaySubject } from 'rxjs'; 293 | 294 | // Create a ReplaySubject that replays the last 2 values 295 | const subject = new ReplaySubject(2); 296 | 297 | // Emit values to the ReplaySubject 298 | subject.next(1); 299 | subject.next(2); 300 | subject.next(3); 301 | 302 | // Subscribe to the ReplaySubject 303 | subject.subscribe(value => console.log('Subscriber:', value)); 304 | 305 | // Output: Subscriber: 2 (replays the last 2 emitted values) 306 | // Output: Subscriber: 3 307 | 308 | // Emit another value 309 | subject.next(4); 310 | 311 | // Output: Subscriber: 4 312 | 313 | ``` 314 | 315 | 3. **AsyncSubject**: 316 | - `AsyncSubject` is a variation of `Subject` that only emits the last value when it completes. It waits until the `complete()` method is called to emit the final value, discarding any previous values. 317 | - `AsyncSubject` is useful when you only need the final value from an asynchronous operation or stream. 318 | - Example: 319 | 320 | ```jsx 321 | import { AsyncSubject } from 'rxjs'; 322 | 323 | // Create an AsyncSubject 324 | const subject = new AsyncSubject(); 325 | 326 | // Subscribe to the AsyncSubject 327 | subject.subscribe(value => console.log('Subscriber:', value)); 328 | 329 | // Output: (no output yet) 330 | 331 | // Emit values to the AsyncSubject 332 | subject.next(1); 333 | subject.next(2); 334 | subject.next(3); 335 | 336 | // Complete the AsyncSubject 337 | subject.complete(); 338 | 339 | // Output: Subscriber: 3 (emits the final value after completion) 340 | 341 | ``` 342 | 343 | 344 | These variations of `Subject` provide specialized functionalities to cater to different use cases. Understanding their behavior and characteristics allows you to choose the appropriate subject type based on your specific requirements. 345 | 346 | 358 | 359 | --- 360 | 361 | # More on Observable and Subject 362 | 363 | 379 | 380 | 386 | 387 | ### Observable : 388 | 389 | Certainly! Here's an example that demonstrates how each subscriber to an `Observable` gets its own independent execution of the observable logic: 390 | 391 | ```jsx 392 | import { Observable } from 'rxjs'; 393 | 394 | // Create an Observable 395 | const observable = new Observable(observer => { 396 | console.log('Observable logic started'); 397 | let count = 0; 398 | 399 | const intervalId = setInterval(() => { 400 | observer.next(count++); 401 | }, 1000); 402 | 403 | return () => { 404 | console.log('Observable logic stopped'); 405 | clearInterval(intervalId); 406 | }; 407 | }); 408 | 409 | // Subscribe to the Observable with Subscriber A 410 | const subscriberA = observable.subscribe(value => { 411 | console.log('Subscriber A:', value); 412 | }); 413 | 414 | // After 3 seconds, subscribe to the Observable with Subscriber B 415 | setTimeout(() => { 416 | const subscriberB = observable.subscribe(value => { 417 | console.log('Subscriber B:', value); 418 | }); 419 | }, 3000); 420 | 421 | ``` 422 | 423 | In this example, we create an `Observable` that emits incrementing values every second using `observer.next()`. Each subscriber to the `Observable` will receive these values independently. 424 | 425 | When we subscribe with "Subscriber A", the observable logic starts, and it logs `'Observable logic started'`. "Subscriber A" starts receiving values emitted by the observable. 426 | 427 | After 3 seconds, we subscribe with "Subscriber B". At this point, a new independent execution of the observable logic starts, and "Subscriber B" will receive values separately from "Subscriber A". 428 | 429 | If we were to unsubscribe "Subscriber A" or "Subscriber B" by calling `subscriberA.unsubscribe()` or `subscriberB.unsubscribe()`, respectively, the corresponding observable logic would stop, and it would log `'Observable logic stopped'`. 430 | 431 | This example illustrates that each subscriber to an `Observable` has its own independent execution of the observable logic. They can receive values concurrently and unsubscribe individually without affecting other subscribers. 432 | 433 | This is what output looks like 👇 434 | 435 | ![Untitled](assets/Untitled.png) 436 | 437 | ### Subject : 438 | 439 | 1. Centralized Event Bus: `Subject` allows you to create a centralized event bus or message broker where different parts of your application can publish and subscribe to events. It enables decoupled communication between components or services. 440 | 2. Late Subscription: With `Subject`, you can subscribe to it at any point in time and still receive the latest values emitted. This can be useful when you want to ensure that a subscriber receives the most up-to-date information, even if they subscribe after some events have already occurred. 441 | 3. Manual Control: `Subject` provides explicit methods to emit values (`next`), handle errors (`error`), and mark the completion of the stream (`complete`). This gives you more control over when and how values are emitted to subscribers. 442 | 4. Broadcasting to Multiple Subscribers: Unlike regular `Observable`, where each subscriber gets an independent execution of the observable logic, `Subject` allows you to broadcast the same set of values to multiple subscribers simultaneously. 443 | 5. State Management: `Subject` can be used as a simple state management tool, where subscribers receive updates whenever the state changes. This can be useful for scenarios where multiple components need to stay in sync with a shared state. 444 | 445 | --- 446 | 447 | ## 3. Combining Observables 448 | 449 | Certainly! Here are examples of combining observables using `combineLatest`, `concat`, `merge`, and `zip`: 450 | 451 | 1. **combineLatest** : 452 | - `combineLatest` combines multiple observables into a single observable that emits an array of the latest values from each source observable whenever any of the source observables emit a new value. 453 | - Example: 454 | 455 | ```jsx 456 | import { combineLatest, interval } from 'rxjs'; 457 | 458 | const observable1 = interval(1000); // Emits values 0, 1, 2, ... 459 | const observable2 = interval(2000); // Emits values 0, 1, 2, ... 460 | 461 | const combined = combineLatest([observable1, observable2]); 462 | 463 | combined.subscribe(([value1, value2]) => { 464 | console.log('Combined:', value1, value2); 465 | }); 466 | 467 | // Output: 468 | // Combined: 0 0 469 | // Combined: 1 0 470 | // Combined: 1 1 471 | // Combined: 2 1 472 | // Combined: 2 2 473 | // ... 474 | ``` 475 | 476 | 2. **concat** : 477 | - `concat` concatenates multiple observables sequentially, emitting values in the order they are provided. It subscribes to each observable one after another, waiting for each to complete before moving to the next. 478 | - Example: 479 | 480 | ```jsx 481 | import { concat, interval } from 'rxjs'; 482 | import { take } from 'rxjs/operators'; 483 | 484 | const observable1 = interval(1000).pipe(take(3)); // Emits values 0, 1, 2 485 | const observable2 = interval(500).pipe(take(2)); // Emits values 0, 1 486 | 487 | const concatenated = concat(observable1, observable2); 488 | 489 | concatenated.subscribe(value => { 490 | console.log('Concatenated:', value); 491 | }); 492 | 493 | // Output: 494 | // Concatenated: 0 495 | // Concatenated: 1 496 | // Concatenated: 2 497 | // Concatenated: 0 498 | // Concatenated: 1 499 | ``` 500 | 501 | 3. **merge** : 502 | - `merge` merges multiple observables into a single observable, emitting values as they are emitted from any source observable. Values from different observables can interleave. 503 | - Example: 504 | 505 | ```jsx 506 | import { merge, interval } from 'rxjs'; 507 | 508 | const observable1 = interval(1000); // Emits values 0, 1, 2, ... 509 | const observable2 = interval(1500); // Emits values 0, 1, 2, ... 510 | 511 | const merged = merge(observable1, observable2); 512 | 513 | merged.subscribe(value => { 514 | console.log('Merged:', value); 515 | }); 516 | 517 | // Output: 518 | // Merged: 0 519 | // Merged: 0 520 | // Merged: 1 521 | // Merged: 2 522 | // Merged: 1 523 | // Merged: 3 524 | // ... 525 | ``` 526 | 527 | 4. **zip :** 528 | - `zip` combines multiple observables into a single observable by emitting values pairwise based on their order. It waits for all source observables to emit a value before emitting an array of the combined values. 529 | - Example: 530 | 531 | ```jsx 532 | import { zip, interval } from 'rxjs'; 533 | import { take } from 'rxjs/operators'; 534 | 535 | const observable1 = interval(1000).pipe(take(3)); // Emits values 0, 1, 2 536 | const observable2 = interval(1500).pipe(take(3)); // Emits values 0, 1, 2 537 | 538 | const zipped = zip(observable1, observable2); 539 | 540 | zipped.subscribe(([value1, value2]) => { 541 | console.log('Zipped:', value1, value2); 542 | }); 543 | 544 | // Output: 545 | // Zipped: 0 0 546 | // Zipped: 1 1 547 | // Zipped: 2 2 548 | ``` 549 | 550 | 551 | Some More down here 👇 552 | 553 | 1. `forkJoin`: 554 | 555 | Waits for all source observables to complete and then emits an array of the last values from each observable. It is used when you need to combine the results of multiple observables into a single emission. 556 | 557 | ```jsx 558 | import { forkJoin, of, timer } from 'rxjs'; 559 | 560 | const observable1 = of('Hello'); 561 | const observable2 = of('World'); 562 | const observable3 = timer(2000); 563 | 564 | forkJoin([observable1, observable2, observable3]).subscribe(([value1, value2]) => { 565 | console.log('ForkJoined:', value1, value2); 566 | }); 567 | 568 | // Output: 569 | // ForkJoined: Hello World (after 2 seconds) 570 | 571 | ``` 572 | 573 | 2. `race`: 574 | 575 | Takes multiple observables and emits the values from 576 | the observable that emits first. It is used when you want to take the 577 | value from the observable that responds first and ignore the rest. 578 | 579 | ```jsx 580 | import { race, interval } from 'rxjs'; 581 | 582 | const observable1 = interval(1000); 583 | const observable2 = interval(1500); 584 | 585 | race(observable1, observable2).subscribe(value => { 586 | console.log('Race:', value); 587 | }); 588 | 589 | // Output: 590 | // Race: 0 (from the faster observable) 591 | // Race: 1 592 | // Race: 2 593 | // ... 594 | ``` 595 | 596 | 3. `withLatestFrom`: 597 | 598 | Combines the latest values from the source 599 | observable with values from other observables, producing a new value 600 | whenever the source observable emits. It is used when you need to 601 | combine the latest values from multiple observables and perform some 602 | logic based on those values. 603 | 604 | ```jsx 605 | import { withLatestFrom, interval } from 'rxjs'; 606 | 607 | const source = interval(1000); 608 | const secondObservable = interval(2000); 609 | 610 | source.pipe(withLatestFrom(secondObservable)).subscribe(([value1, value2]) => { 611 | console.log('WithLatestFrom:', value1, value2); 612 | }); 613 | 614 | // Output: 615 | // WithLatestFrom: 0 0 616 | // WithLatestFrom: 1 0 617 | // WithLatestFrom: 2 1 618 | // WithLatestFrom: 3 1 619 | // ... 620 | ``` 621 | 622 | 4. `startWith`: 623 | 624 | Prepends a specified value to the sequence of 625 | values emitted by the source observable. It is used when you want to add 626 | an initial value before the observable starts emitting its regular 627 | values. 628 | 629 | ```jsx 630 | import { of } from 'rxjs'; 631 | import { startWith } from 'rxjs/operators'; 632 | 633 | const source = of('World'); 634 | 635 | source.pipe(startWith('Hello')).subscribe(value => { 636 | console.log('StartWith:', value); 637 | }); 638 | 639 | // Output: 640 | // StartWith: Hello 641 | // StartWith: World 642 | ``` 643 | 644 | 5. `combineAll`: 645 | 646 | Combines a higher-order observable by emitting 647 | an array of the most recent values from each nested observable when any 648 | of the nested observables emit a value. It is used when you have an 649 | observable that emits other observables, and you want to combine the 650 | latest values from each nested observable. 651 | 652 | ```jsx 653 | import { combineAll, interval, take } from 'rxjs'; 654 | 655 | const source = interval(1000).pipe(take(3)); 656 | const higherOrderObservable = source.pipe(map(value => interval(500).pipe(take(2)))); 657 | 658 | higherOrderObservable.pipe(combineAll()).subscribe(value => { 659 | console.log('CombineAll:', value); 660 | }); 661 | 662 | // Output: 663 | // CombineAll: [0, 0] 664 | // CombineAll: [1, 0] 665 | // CombineAll: [2, 0] 666 | ``` 667 | 668 | 6. `switch`: 669 | 670 | Converts a higher-order observable into a first-order observable by subscribing to the most recently emitted inner observable and emitting its values. It is used when you have an observable that emits other observables, and you want to switch to the latest emitted observable and receive its values. 671 | 672 | ```jsx 673 | import { interval, of } from 'rxjs'; 674 | import { switchAll } from 'rxjs/operators'; 675 | 676 | const source = interval(1000).pipe(take(3)); 677 | const higherOrderObservable = source.pipe(map(value => interval(500).pipe(take(2)))); 678 | 679 | higherOrderObservable.pipe(switchAll()).subscribe(value => { 680 | console.log('Switched:', value); 681 | }); 682 | 683 | // Output: 684 | // Switched: 0 (from the first inner observable) 685 | // Switched: 1 686 | // Switched: 0 (from the second inner observable) 687 | // Switched: 1 688 | 689 | ``` 690 | 691 | 7. `exhaust`: 692 | 693 | Ignores new source observables while an inner observable is still active. Once the active inner observable completes, it subscribes to the next available inner observable. It is used when you want to ignore new observables until the current inner observable completes. 694 | 695 | ```jsx 696 | import { interval } from 'rxjs'; 697 | import { exhaust, take } from 'rxjs/operators'; 698 | 699 | const source = interval(1000).pipe(take(3)); 700 | const higherOrderObservable = source.pipe(map(value => 701 | interval(500).pipe(take(2)) 702 | )); 703 | 704 | higherOrderObservable.pipe(exhaust()).subscribe(value => { 705 | console.log('Exhausted:', value); 706 | }); 707 | 708 | // Output: 709 | // Exhausted: 0 (from the first inner observable) 710 | // Exhausted: 1 711 | // Exhausted: 2 712 | ``` 713 | 714 | 8. `mergeMap` (or `flatMap`): 715 | 716 | Maps each value from the source observable to an inner observable, subscribes to all inner observables, and emits values from all of them. It is used when you need to map values to inner observables and combine their emissions into a single observable stream. 717 | 718 | ```jsx 719 | import { of } from 'rxjs'; 720 | import { mergeMap } from 'rxjs/operators'; 721 | 722 | const source = of('Hello', 'World'); 723 | 724 | source.pipe(mergeMap(value => of(`${value}!`))).subscribe(value => { 725 | console.log('Merged:', value); 726 | }); 727 | 728 | // Output: 729 | // Merged: Hello! 730 | // Merged: World! 731 | ``` 732 | 733 | 734 | 1. `concatMap`: 735 | 736 | Maps each value from the source observable to an inner observable, subscribes to them sequentially, and emits values in the order they are emitted by the inner observables. It is used when you want to preserve the order of emissions from the inner observables. 737 | 738 | ```jsx 739 | import { interval } from 'rxjs'; 740 | import { concatMap, take } from 'rxjs/operators'; 741 | 742 | const source = interval(1000).pipe(take(3)); 743 | 744 | source.pipe(concatMap(value => interval(500).pipe(take(2)))).subscribe(value => { 745 | console.log('Concatenated:', value); 746 | }); 747 | 748 | // Output: 749 | // Concatenated: 0 (from the first inner observable) 750 | // Concatenated: 1 751 | // Concatenated: 0 (from the second inner observable) 752 | // Concatenated: 1 753 | ``` 754 | 755 | 2. `switchMap`: 756 | 757 | Maps each value from the source observable to an inner observable, subscribes to the inner observable, and emits values from the most recently subscribed inner observable. It is used when you want to switch to a new inner observable whenever a new value arrives, canceling the previous inner observable subscription. 758 | 759 | ```jsx 760 | import { interval } from 'rxjs'; 761 | import { switchMap, take } from 'rxjs/operators'; 762 | 763 | const source = interval(1000).pipe(take(3)); 764 | 765 | source.pipe(switchMap(value => interval(500).pipe(take(2)))).subscribe(value => { 766 | console.log('Switched:', value); 767 | }); 768 | 769 | // Output: 770 | // Switched: 0 (from the first inner observable) 771 | // Switched: 1 772 | // Switched: 0 (from the second inner observable) 773 | // Switched: 1 774 | 775 | ``` 776 | 777 | 778 | --- 779 | 780 | ## 4. Transformation Operators 781 | 782 | 1. `map`: Applies a projection function to each value emitted by the source observable and emits the transformed values. 783 | Example: 784 | 785 | ```jsx 786 | import { of } from 'rxjs'; 787 | import { map } from 'rxjs/operators'; 788 | 789 | const source = of(1, 2, 3); 790 | 791 | source.pipe(map(value => value * 2)).subscribe(result => { 792 | console.log(result); 793 | }); 794 | 795 | // Output: 796 | // 2 797 | // 4 798 | // 6 799 | ``` 800 | 801 | 2. `pluck`: Retrieves a specified nested property from each value emitted by the source observable. 802 | Example: 803 | 804 | ```jsx 805 | import { of } from 'rxjs'; 806 | import { pluck } from 'rxjs/operators'; 807 | 808 | const source = of({ name: 'John', age: 30 }, { name: 'Jane', age: 25 }); 809 | 810 | source.pipe(pluck('name')).subscribe(result => { 811 | console.log(result); 812 | }); 813 | 814 | // Output: 815 | // John 816 | // Jane 817 | 818 | ``` 819 | 820 | 3. `mapTo`: Maps every value emitted by the source observable to a constant value. 821 | Example: 822 | 823 | ```jsx 824 | import { of } from 'rxjs'; 825 | import { mapTo } from 'rxjs/operators'; 826 | 827 | const source = of(1, 2, 3); 828 | 829 | source.pipe(mapTo('Hello')).subscribe(result => { 830 | console.log(result); 831 | }); 832 | 833 | // Output: 834 | // Hello 835 | // Hello 836 | // Hello 837 | 838 | ``` 839 | 840 | 4. `filter`: Filters values emitted by the source observable based on a condition. Only the values that satisfy the condition are passed through to the resulting observable. 841 | Example: 842 | 843 | ```jsx 844 | import { of } from 'rxjs'; 845 | import { filter } from 'rxjs/operators'; 846 | 847 | const source = of(1, 2, 3, 4, 5); 848 | 849 | source.pipe(filter(value => value % 2 === 0)).subscribe(result => { 850 | console.log(result); 851 | }); 852 | 853 | // Output: 854 | // 2 855 | // 4 856 | ``` 857 | 858 | 5. `take`: Takes a specified number of values emitted by the source observable and completes the observable. 859 | Example: 860 | 861 | ```jsx 862 | import { interval } from 'rxjs'; 863 | import { take } from 'rxjs/operators'; 864 | 865 | const source = interval(1000); 866 | 867 | source.pipe(take(3)).subscribe(result => { 868 | console.log(result); 869 | }); 870 | 871 | // Output: 872 | // 0 873 | // 1 874 | // 2 875 | ``` 876 | 877 | 6. `takeLast`: Takes the last specified number of values emitted by the source observable and completes the observable. 878 | Example: 879 | 880 | ```jsx 881 | import { of } from 'rxjs'; 882 | import { takeLast } from 'rxjs/operators'; 883 | 884 | const source = of(1, 2, 3, 4, 5); 885 | 886 | source.pipe(takeLast(3)).subscribe(result => { 887 | console.log(result); 888 | }); 889 | 890 | // Output: 891 | // 3 892 | // 4 893 | // 5 894 | ``` 895 | 896 | 7. `takeWhile`: Takes values emitted by the source observable until the provided condition becomes false. It completes the observable when the condition fails. 897 | Example: 898 | 899 | ```jsx 900 | import { interval } from 'rxjs'; 901 | import { takeWhile } from 'rxjs/operators'; 902 | 903 | const source = interval(1000); 904 | 905 | source.pipe(takeWhile(value => value < 5)).subscribe(result => { 906 | console.log(result); 907 | }); 908 | 909 | // Output: 910 | // 0 911 | // 1 912 | // 2 913 | // 3 914 | // 4 915 | ``` 916 | 917 | 8. `takeUntil`: Takes values emitted by the source observable until another observable emits a value. It completes the observable when the other observable emits. 918 | Example: 919 | 920 | ```jsx 921 | import { interval, timer } from 'rxjs'; 922 | import { takeUntil } from 'rxjs/operators'; 923 | 924 | const source = interval(1000); 925 | const timerObservable = timer(5000); 926 | 927 | source.pipe(takeUntil(timerObservable)).subscribe(result => { 928 | console.log(result); 929 | }); 930 | 931 | // Output: 932 | // 0 933 | // 1 934 | // 2 935 | // 3 936 | // 4 937 | ``` 938 | 939 | 940 | 1. `first`: Emits only the first value emitted by the source observable and completes the observable. 941 | Example: 942 | 943 | ```jsx 944 | import { of } from 'rxjs'; 945 | import { first } from 'rxjs/operators'; 946 | 947 | const source = of(1, 2, 3, 4, 5); 948 | 949 | source.pipe(first()).subscribe(result => { 950 | console.log(result); 951 | }); 952 | 953 | // Output: 954 | // 1 955 | ``` 956 | 957 | 2. `last`: Emits only the last value emitted by the source observable and completes the observable. 958 | Example: 959 | 960 | ```jsx 961 | import { of } from 'rxjs'; 962 | import { last } from 'rxjs/operators'; 963 | 964 | const source = of(1, 2, 3, 4, 5); 965 | 966 | source.pipe(last()).subscribe(result => { 967 | console.log(result); 968 | }); 969 | 970 | // Output: 971 | // 5 972 | ``` 973 | 974 | 3. `skip`: Skips a specified number of values emitted by the source observable and emits the rest. 975 | Example: 976 | 977 | ```jsx 978 | import { of } from 'rxjs'; 979 | import { skip } from 'rxjs/operators'; 980 | 981 | const source = of(1, 2, 3, 4, 5); 982 | 983 | source.pipe(skip(2)).subscribe(result => { 984 | console.log(result); 985 | }); 986 | 987 | // Output: 988 | // 3 989 | // 4 990 | // 5 991 | 992 | ``` 993 | 994 | 4. `skipLast`: Skips the last specified number of values emitted by the source observable and emits the rest. 995 | Example: 996 | 997 | ```jsx 998 | import { of } from 'rxjs'; 999 | import { skipLast } from 'rxjs/operators'; 1000 | 1001 | const source = of(1, 2, 3, 4, 5); 1002 | 1003 | source.pipe(skipLast(2)).subscribe(result => { 1004 | console.log(result); 1005 | }); 1006 | 1007 | // Output: 1008 | // 1 1009 | // 2 1010 | // 3 1011 | ``` 1012 | 1013 | 5. `skipWhile`: Skips values emitted by the source observable until the provided condition becomes false, and emits the rest. 1014 | Example: 1015 | 1016 | ```jsx 1017 | import { of } from 'rxjs'; 1018 | import { skipWhile } from 'rxjs/operators'; 1019 | 1020 | const source = of(1, 2, 3, 4, 5); 1021 | 1022 | source.pipe(skipWhile(value => value < 3)).subscribe(result => { 1023 | console.log(result); 1024 | }); 1025 | 1026 | // Output: 1027 | // 3 1028 | // 4 1029 | // 5 1030 | ``` 1031 | 1032 | 6. `skipUntil`: Skips values emitted by the source observable until another observable emits a value, and emits the rest. 1033 | Example: 1034 | 1035 | ```jsx 1036 | import { interval, timer } from 'rxjs'; 1037 | import { skipUntil } from 'rxjs/operators'; 1038 | 1039 | const source = interval(1000); 1040 | const timerObservable = timer(3000); 1041 | 1042 | source.pipe(skipUntil(timerObservable)).subscribe(result => { 1043 | console.log(result); 1044 | }); 1045 | 1046 | // Output: 1047 | // 2 1048 | // 3 1049 | // 4 1050 | // ... 1051 | 1052 | ``` 1053 | 1054 | 7. `distinct`: Filters out duplicate consecutive values emitted by the source observable. 1055 | Example: 1056 | 1057 | ```jsx 1058 | import { of } from 'rxjs'; 1059 | import { distinct } from 'rxjs/operators'; 1060 | 1061 | const source = of(1, 2, 2, 3, 3, 3, 4, 5, 5); 1062 | 1063 | source.pipe(distinct()).subscribe(result => { 1064 | console.log(result); 1065 | }); 1066 | 1067 | // Output: 1068 | // 1 1069 | // 2 1070 | // 3 1071 | // 4 1072 | // 5 1073 | 1074 | ``` 1075 | 1076 | 8. `distinctUntilChanged`: Filters out consecutive values emitted by the source observable that are equal to the previous value. 1077 | Example: 1078 | 1079 | ```jsx 1080 | import { of } from 'rxjs'; 1081 | import { distinctUntilChanged } from 'rxjs/operators'; 1082 | 1083 | const source = of(1, 1, 2, 2, 3, 4, 4, 5, 5); 1084 | 1085 | source.pipe(distinctUntilChanged()).subscribe(result => { 1086 | console.log(result); 1087 | }); 1088 | 1089 | // Output: 1090 | // 1 1091 | // 2 1092 | // 3 1093 | // 4 1094 | // 5 1095 | 1096 | ``` 1097 | 1098 | 9. `distinctUntilKeyChanged`: Filters out consecutive values emitted by the source observable based on a specified key's equality comparison. 1099 | Example: 1100 | 1101 | ```jsx 1102 | import { of } 1103 | from 'rxjs'; 1104 | import { distinctUntilKeyChanged } from 'rxjs/operators'; 1105 | 1106 | const source = of( 1107 | { id: 1, name: 'John' }, 1108 | { id: 2, name: 'John' }, 1109 | { id: 2, name: 'Jane' }, 1110 | { id: 3, name: 'Jane' } 1111 | ); 1112 | 1113 | source.pipe(distinctUntilKeyChanged('name')).subscribe(result => { 1114 | console.log(result); 1115 | }); 1116 | 1117 | // Output: 1118 | // { id: 1, name: 'John' } 1119 | // { id: 2, name: 'Jane' } 1120 | ``` 1121 | 1122 | 1123 | 1. `debounceTime`: Emits a value from the source observable only after a specified time has passed without any new values being emitted. 1124 | Example: 1125 | 1126 | ```jsx 1127 | import { fromEvent } from 'rxjs'; 1128 | import { debounceTime } from 'rxjs/operators'; 1129 | 1130 | const button = document.getElementById('button'); 1131 | const clicks = fromEvent(button, 'click'); 1132 | 1133 | clicks.pipe(debounceTime(1000)).subscribe(() => { 1134 | console.log('Clicked after 1 second of inactivity'); 1135 | }); 1136 | 1137 | // Output: 1138 | // Clicked after 1 second of inactivity 1139 | ``` 1140 | 1141 | 1142 | 1. `throttleTime`: Emits the first value from the source observable, and then ignores subsequent values for a specified time period. 1143 | Example: 1144 | 1145 | ```jsx 1146 | import { interval } from 'rxjs'; 1147 | import { throttleTime } from 'rxjs/operators'; 1148 | 1149 | const source = interval(1000); 1150 | 1151 | source.pipe(throttleTime(2000)).subscribe(result => { 1152 | console.log(result); 1153 | }); 1154 | 1155 | // Output: 1156 | // 0 1157 | // 3 1158 | // 6 1159 | // ... 1160 | ``` 1161 | 1162 | 2. `buffer`: Collects multiple values emitted by the source observable and emits them as an array whenever a specified signal observable emits. 1163 | Example: 1164 | 1165 | ```jsx 1166 | import { interval, fromEvent } from 'rxjs'; 1167 | import { buffer } from 'rxjs/operators'; 1168 | 1169 | const source = interval(1000); 1170 | const clickSignal = fromEvent(document, 'click'); 1171 | 1172 | source.pipe(buffer(clickSignal)).subscribe(result => { 1173 | console.log(result); 1174 | }); 1175 | 1176 | // Output (When clicking the document): 1177 | // [0, 1, 2] 1178 | // [3, 4, 5, 6] 1179 | // [7, 8] 1180 | // ... 1181 | ``` 1182 | 1183 | 1184 | 1. `bufferTime`: Collects values emitted by the source observable over a specified time period and emits them as an array. 1185 | Example: 1186 | 1187 | ```jsx 1188 | import { interval } from 'rxjs'; 1189 | import { bufferTime } from 'rxjs/operators'; 1190 | 1191 | const source = interval(1000); 1192 | 1193 | source.pipe(bufferTime(3000)).subscribe(result => { 1194 | console.log(result); 1195 | }); 1196 | 1197 | // Output: 1198 | // [0, 1, 2] 1199 | // [3, 4, 5] 1200 | // [6, 7, 8] 1201 | // ... 1202 | 1203 | ``` 1204 | 1205 | 2. `scan`: Applies an accumulator function to the values emitted by the source observable and emits the accumulated result after each emission. 1206 | Example: 1207 | 1208 | ```jsx 1209 | import { of } from 'rxjs'; 1210 | import { scan } from 'rxjs/operators'; 1211 | 1212 | const source = of(1, 2, 3, 4, 5); 1213 | 1214 | source.pipe(scan((acc, value) => acc + value)).subscribe(result => { 1215 | console.log(result); 1216 | }); 1217 | 1218 | // Output: 1219 | // 1 1220 | // 3 1221 | // 6 1222 | // 10 1223 | // 15 1224 | ``` 1225 | 1226 | 3. `reduce`: Accumulates values emitted by the source observable and emits the accumulated result. 1227 | Example: 1228 | 1229 | ```jsx 1230 | import { of } from 'rxjs'; 1231 | import { reduce } from 'rxjs/operators'; 1232 | 1233 | const source = of(1, 2, 3, 4, 5); 1234 | 1235 | source.pipe(reduce((acc, value) => acc + value)).subscribe(result => { 1236 | console.log(result); 1237 | }); 1238 | 1239 | // Output: 1240 | // 15 1241 | ``` 1242 | 1243 | 1244 | --- 1245 | 1246 | ## 5. Error Handling 1247 | 1248 | 1. `catchError`: Catches errors emitted by the source observable and replaces them with a fallback observable or value. 1249 | Example: 1250 | 1251 | ```jsx 1252 | import { of, throwError } from 'rxjs'; 1253 | import { catchError } from 'rxjs/operators'; 1254 | 1255 | const source = of(1, 2, 3, throwError('Error'), 5); 1256 | 1257 | source.pipe( 1258 | catchError(error => { 1259 | console.log('Caught error:', error); 1260 | return of('Fallback Value'); 1261 | }) 1262 | ).subscribe(result => { 1263 | console.log(result); 1264 | }); 1265 | 1266 | // Output: 1267 | // 1 1268 | // 2 1269 | // 3 1270 | // Caught error: Error 1271 | // Fallback Value 1272 | // 5 1273 | ``` 1274 | 1275 | Use case: `catchError` is commonly used to handle errors gracefully and provide fallback behavior. It allows you to handle specific error scenarios and recover by returning a fallback observable or value. 1276 | 1277 | 2. `retry`: Resubscribes to the source observable when it encounters an error, allowing for retry attempts. 1278 | Example: 1279 | 1280 | ```jsx 1281 | import { of, throwError } from 'rxjs'; 1282 | import { retry } from 'rxjs/operators'; 1283 | 1284 | let attempts = 0; 1285 | 1286 | const source = of('Initial Value', throwError('Error')).pipe( 1287 | retry(3) 1288 | ); 1289 | 1290 | source.subscribe( 1291 | result => console.log(result), 1292 | error => console.log('Caught error after retry attempts:', error) 1293 | ); 1294 | 1295 | // Output: 1296 | // Initial Value 1297 | // Initial Value 1298 | // Initial Value 1299 | // Caught error after retry attempts: Error 1300 | ``` 1301 | 1302 | Use case: `retry` is useful in scenarios where you want to retry the execution of an observable sequence after encountering an error. It allows for a specified number of retry attempts to handle temporary failures or intermittent errors. 1303 | 1304 | 3. `retryWhen`: Resubscribes to the source observable when it encounters an error based on a provided notifier observable. 1305 | Example: 1306 | 1307 | ```jsx 1308 | import { of, throwError, timer } from 'rxjs'; 1309 | import { retryWhen, mergeMap } from 'rxjs/operators'; 1310 | 1311 | const source = of('Initial Value', throwError('Error')).pipe( 1312 | retryWhen(errors => errors.pipe( 1313 | mergeMap((error, index) => { 1314 | if (index < 2) { 1315 | return timer(1000); // Retry after 1 second 1316 | } 1317 | return throwError('Max retry attempts reached'); 1318 | }) 1319 | )) 1320 | ); 1321 | 1322 | source.subscribe( 1323 | result => console.log(result), 1324 | error => console.log('Caught error:', error) 1325 | ); 1326 | 1327 | // Output: 1328 | // Initial Value 1329 | // Initial Value 1330 | // Initial Value 1331 | // Caught error: Max retry attempts reached 1332 | ``` 1333 | 1334 | Use case: `retryWhen` is useful when you want to dynamically control the retry behavior based on a notifier observable. It allows for more advanced retry strategies, such as delaying retries, limiting the number of retries, or applying custom logic for determining whether to retry or not. 1335 | 1336 | 4. `onErrorResumeNext`: Ignores errors from the source observable and subscribes to the next observable in the sequence. 1337 | Example: 1338 | 1339 | ```jsx 1340 | import { of, throwError } from 'rxjs'; 1341 | import { onErrorResumeNext } from 'rxjs/operators'; 1342 | 1343 | const source1 = of(1, 2, throwError('Error 1')); 1344 | const source2 = of(3, 4, 5); 1345 | 1346 | source1.pipe( 1347 | onErrorResumeNext(source2) 1348 | ).subscribe(result => { 1349 | console.log(result); 1350 | }); 1351 | 1352 | // Output: 1353 | // 1 1354 | // 2 1355 | 1356 | // 3 1357 | // 4 1358 | // 5 1359 | ``` 1360 | 1361 | Use case: `onErrorResumeNext` is useful when you have multiple observables and want to continue the sequence by subscribing to the next observable even if an error occurs. It allows for graceful error handling and seamless switching between observables. 1362 | 1363 | 5. `throwError`: Creates an observable that immediately emits an error using a specified error message or error object. 1364 | Example: 1365 | 1366 | ```jsx 1367 | import { throwError } from 'rxjs'; 1368 | 1369 | throwError('This is an error').subscribe({ 1370 | error: error => { 1371 | console.log('Caught error:', error); 1372 | } 1373 | }); 1374 | 1375 | // Output: 1376 | // Caught error: This is an error 1377 | 1378 | ``` 1379 | 1380 | Use case: `throwError` is used to create an observable that emits an error immediately. It is often used in scenarios where you want to explicitly throw an error within an observable sequence. 1381 | 1382 | 6. `finalize`: Performs a specified action when the source observable completes, errors, or gets unsubscribed. 1383 | Example: 1384 | 1385 | ```jsx 1386 | import { of } from 'rxjs'; 1387 | import { finalize } from 'rxjs/operators'; 1388 | 1389 | const source = of(1, 2, 3); 1390 | 1391 | source.pipe( 1392 | finalize(() => { 1393 | console.log('Finalize action'); 1394 | }) 1395 | ).subscribe(result => { 1396 | console.log(result); 1397 | }); 1398 | 1399 | // Output: 1400 | // 1 1401 | // 2 1402 | // 3 1403 | // Finalize action 1404 | ``` 1405 | 1406 | Use case: `finalize` is useful when you want to perform a specific action when the observable completes, errors, or gets unsubscribed. It allows for cleanup operations, resource releasing, or logging activities to be performed regardless of the observable's outcome. 1407 | 1408 | 1409 | # Advance Concepts in RXJS 1410 | 1411 | 1. **Backpressure**: Backpressure is a mechanism for handling situations where the rate of emissions from an observable is faster than the rate at which the observer can consume/process those emissions. It helps prevent overwhelming the consumer with a large number of emitted values. 1412 | - Example: Imagine a real-time data stream producing events faster than the consumer can handle. By applying backpressure mechanisms, such as using buffer operators like `bufferCount` or `bufferTime`, you can control the rate at which the consumer processes the events, preventing overload and ensuring smooth processing. 1413 | - Use case: Handling high-frequency event streams, such as sensor data, network packets, or stock market data, where the consumer may need to process or aggregate events at a manageable pace to avoid overwhelming system resources. 1414 | 1415 | ```jsx 1416 | import { interval } from 'rxjs'; 1417 | import { bufferTime } from 'rxjs/operators'; 1418 | 1419 | const source = interval(100); // Emits values every 100ms 1420 | 1421 | source.pipe( 1422 | bufferTime(500) // Buffer values for 500ms 1423 | ).subscribe(bufferedValues => { 1424 | console.log('Buffered Values:', bufferedValues); 1425 | }); 1426 | ``` 1427 | 1428 | 1. **Schedulers**: Schedulers in RxJS provide control over when and where the work associated with an observable is executed. They allow you to define the execution context, such as running the observable's logic on a specific thread, asynchronously, or with a delay. Schedulers help manage concurrency and provide options for fine-grained control over the timing and execution of observables. 1429 | - Example: Using the `observeOn` operator with a scheduler, such as `observeOn(Rx.Scheduler.async)`, you can offload heavy computational or blocking tasks to a separate thread, keeping the main thread responsive and ensuring non-blocking execution. 1430 | - Use case: Performing expensive computations, making network requests, or interacting with I/O-bound operations (e.g., file operations, database queries) asynchronously or on a separate thread to prevent UI freezing or blocking the main event loop. 1431 | 1432 | ```jsx 1433 | import { of, asyncScheduler } from 'rxjs'; 1434 | import { observeOn } from 'rxjs/operators'; 1435 | 1436 | const source = of(1, 2, 3); 1437 | 1438 | source.pipe( 1439 | observeOn(asyncScheduler) // Execute on async scheduler 1440 | ).subscribe(value => { 1441 | console.log('Value:', value); 1442 | }); 1443 | ``` 1444 | 1445 | 1446 | 1. **Time-based operators**: Time-based operators in RxJS allow you to work with time-related events, such as delaying emissions, setting intervals between emissions, or defining time windows for aggregating values. These operators enable you to perform time-dependent operations, schedule actions, and handle time-related scenarios in reactive programming. 1447 | - Example: The `debounceTime` operator is commonly used in search input fields, where you delay the emission of values until the user pauses typing for a certain duration. This reduces unnecessary intermediate emissions and improves the efficiency of triggering search requests. 1448 | - Use case: Throttling user input events, scheduling periodic updates or notifications, handling time-sensitive operations, or creating time-based animations in graphical user interfaces. 1449 | 1450 | ```jsx 1451 | import { interval } from 'rxjs'; 1452 | import { debounceTime } from 'rxjs/operators'; 1453 | 1454 | const source = interval(200); // Emits values every 200ms 1455 | 1456 | source.pipe( 1457 | debounceTime(500) // Debounce emissions for 500ms 1458 | ).subscribe(value => { 1459 | console.log('Debounced Value:', value); 1460 | }); 1461 | ``` 1462 | 1463 | 1464 | 1. **Subjects**: Subjects in RxJS are special types of observables that act as both an observer and an observable. They can multicast (or broadcast) values to multiple subscribers, making them suitable for scenarios where you want to distribute values to multiple observers. Subjects maintain an internal list of subscribers and can emit values to all subscribers simultaneously. 1465 | - Example: Imagine a chat application where multiple users can send and receive messages. You can use a subject to represent the chat room, where each new message is emitted by the subject and received by all subscribers (users) simultaneously. 1466 | - Use case: Implementing event bus systems, pub-sub architectures, real-time messaging systems, or any scenario where you need to distribute events or data to multiple subscribers in real-time. 1467 | 1468 | ```jsx 1469 | import { Subject } from 'rxjs'; 1470 | 1471 | const subject = new Subject(); 1472 | 1473 | subject.subscribe(value => { 1474 | console.log('Subscriber A:', value); 1475 | }); 1476 | 1477 | subject.subscribe(value => { 1478 | console.log('Subscriber B:', value); 1479 | }); 1480 | 1481 | subject.next(1); 1482 | subject.next(2); 1483 | ``` 1484 | 1485 | 1486 | 1. **Multicasting**: Multicasting is the process of sharing a single source observable among multiple subscribers, enabling multiple observers to receive the same set of emitted values. Subjects and multicast operators (e.g., `publish`, `share`, `multicast`) are commonly used for implementing multicasting in RxJS. 1487 | - Example: In a real-time dashboard displaying multiple widgets (e.g., stock ticker, weather updates, news feed), you can use multicasting to share a single source of data among multiple widgets, ensuring consistent and synchronized updates for all subscribers. 1488 | - Use case: Building real-time dashboards, collaborative applications, real-time analytics, or any scenario where multiple components or consumers need access to the same set of data or events. 1489 | 1490 | ```jsx 1491 | import { interval } from 'rxjs'; 1492 | import { multicast, take } from 'rxjs/operators'; 1493 | 1494 | const source = interval(1000).pipe(take(5)); 1495 | 1496 | const multicasted = source.pipe( 1497 | multicast(() => new Subject()) 1498 | ); 1499 | 1500 | multicasted.subscribe(value => { 1501 | console.log('Subscriber A:', value); 1502 | }); 1503 | 1504 | multicasted.subscribe(value => { 1505 | console.log('Subscriber B:', value); 1506 | }); 1507 | 1508 | multicasted.connect(); 1509 | ``` 1510 | 1511 | 1512 | 1. **Hot vs. Cold observables**: Hot observables are those that start emitting values even before any subscriptions are made, and all subscribers receive the same set of values regardless of when they subscribed. Cold observables, on the other hand, start emitting values only after a subscription is made, and each subscriber receives its own independent sequence of values. Subjects and multicast operators create hot observables, while most other observables are cold by default. 1513 | - Example: A cold observable could be a stream of mouse click events, where each subscription receives its own independent sequence of click events. In contrast, a hot observable could be a live audio stream where all subscribers receive the same set of audio data regardless of when they started listening. 1514 | - Use case: Event broadcasting, live data streams, sensor data feeds, or scenarios where multiple subscribers need to receive the same set of values simultaneously. 1515 | 1516 | ```jsx 1517 | import { interval } from 'rxjs'; 1518 | import { take } from 'rxjs/operators'; 1519 | 1520 | const coldObservable = interval(1000).pipe(take(5)); 1521 | 1522 | coldObservable.subscribe(value => { 1523 | console.log('Subscriber A:', value); 1524 | }); 1525 | 1526 | setTimeout(() => { 1527 | coldObservable.subscribe(value => { 1528 | console.log('Subscriber B:', value); 1529 | }); 1530 | }, 3000); 1531 | ``` 1532 | 1533 | 1534 | # Extra’s 1535 | 1536 | ## Pipe 1537 | 1538 | In RxJS, the `pipe` function is a method available on observables and is used to apply multiple operators to the source observable in a declarative and composable manner. 1539 | 1540 | The `pipe` function accepts one or more operator functions as arguments and returns a new observable with the operators applied. It does not mutate the original observable but creates a new observable chain. 1541 | 1542 | The `pipe` function allows you to perform a sequence of transformations, filtering, combining, error handling, or any other operations on an observable in a modular way. It enables you to build complex data flows by chaining operators together, improving code readability and maintainability. 1543 | 1544 | Here's an example of using the `pipe` function to apply multiple operators to an observable: 1545 | 1546 | ```jsx 1547 | import { of } from 'rxjs'; 1548 | import { map, filter, take } from 'rxjs/operators'; 1549 | 1550 | const source = of(1, 2, 3, 4, 5); 1551 | 1552 | source.pipe( 1553 | map(value => value * 2), // Transform each value by multiplying by 2 1554 | filter(value => value > 5), // Filter values greater than 5 1555 | take(2) // Take the first 2 values 1556 | ).subscribe(result => { 1557 | console.log(result); 1558 | }); 1559 | 1560 | // Output: 1561 | // 6 1562 | // 8 1563 | ``` 1564 | 1565 | In this example, the `pipe` function is used to chain the `map`, `filter`, and `take` operators together. The source observable `source` undergoes transformation, filtering, and is limited to two values before being subscribed to. The output is the result of the applied operations on the source observable. 1566 | 1567 | By using the `pipe` function, you can apply a series of operators to an observable in a concise and organized manner, facilitating the construction of complex data flows and enabling better code reuse. 1568 | --------------------------------------------------------------------------------