|
| 1 | +block includes |
| 2 | + include ../_util-fns |
| 3 | + |
| 4 | +:marked |
| 5 | + **Observables** provided by the Reactive Extensions for Javascript (RxJS) library provide applications with an extensive API |
| 6 | + for handling asynchronous and event-based values produced over time. |
| 7 | + |
| 8 | + An application is made up of many different streams of information. Whether it be user input into |
| 9 | + a form, navigating from one route another, making an HTTP request to fetch some data, updating the application view with |
| 10 | + new data as its received, or many other examples, each of these events happen over time. Observables provide a interface to |
| 11 | + handle the many different sources of events and help to transform these events as they flow throughout an application. |
| 12 | + |
| 13 | + This guide will serve as an introductory chapter to Observables, common uses cases for Observables in an Angular application |
| 14 | + and how Observables are used and provided by the Angular framework. |
| 15 | + |
| 16 | + ## Table of Contents |
| 17 | + * [The Observable](#definition "") |
| 18 | + * [Observables and Promises](#promises "") |
| 19 | + * [Observable Lifecycle](#lifecycle "") |
| 20 | + * [Observer](#lifecycle "") |
| 21 | + * [Subscriptions](#subscriptions "") |
| 22 | + * [Creating Observables](#creating "") |
| 23 | + * [Operators](#transforming "") |
| 24 | + * [Error Handling](#error-handling "") |
| 25 | + * [Framework APIs](#apis "") |
| 26 | + * [Impact on Change Detection](#change-detection "") |
| 27 | + * [Further Reading](#reading "") |
| 28 | + |
| 29 | +:marked |
| 30 | + The Observable: a function at its core |
| 31 | + |
| 32 | + An Observable, simply put, is a specific type of function with a specific purpose. Its a function that accepts an `Observer` to produce values and |
| 33 | + returns a function for cancellation. It represents an action that can be performed. This action may be performed right now, or at some point |
| 34 | + in the future. |
| 35 | + |
| 36 | + An action can be anything, from simply "return a constant" to "make an HTTP request". Here’s a simple "action" function that increments a counter |
| 37 | + and returns the new value. |
| 38 | + |
| 39 | + // Example of simple counter |
| 40 | + |
| 41 | + The same functionality can be produced with an Observable. Observables don't return values directly, as they can be produced synchronously asynchronously. |
| 42 | + An Observer is used by an Observable to consume its produced values. |
| 43 | + |
| 44 | + // Example of observable counter |
| 45 | + |
| 46 | + There is a key difference between these two examples. In the first counter example, the results of the action were produced when the function was called. |
| 47 | + In the Observable counter, the Observable was created, with the [Observer](https://en.wikipedia.org/wiki/Observer_pattern) to produce values and the value incremented but the action hasn't been performed yet. |
| 48 | + It represents an action we've defined, but it hasn't been executed. |
| 49 | + |
| 50 | + // Example of subscribing to observable counter |
| 51 | + |
| 52 | + Observable streams are **cold** or **lazy** by nature, meaning that until you invoke them, they will not produce |
| 53 | + any values. Invoking an Observable is done using the `subscribe` method, which makes the Observable **hot** and calls the observer's method to produce |
| 54 | + values. The `subscribe` function also returns an object with an `unsubscribe` from an Observable and no longer receive the values it produces. |
| 55 | + |
| 56 | + // Example of unsubscribing |
| 57 | + |
| 58 | + Managing the lifecycle of an Observable will be discussed later in the chapter. |
| 59 | + |
| 60 | +:marked |
| 61 | + Observables and Promises: More different than alike |
| 62 | + |
| 63 | + RxJS and Observables have been around for a long time, and they aren't the first concept of handling asynchronous events. Before Observables became more prevalent, |
| 64 | + the `Promise` was the primary way of handling asynchronous events. Promises and Observables share some similarities as they both handle asynchronous events, |
| 65 | + both implement a function to handle execution and error handling, but they are more different then alike. |
| 66 | + |
| 67 | + Promises |
| 68 | + * Produce a one-time value |
| 69 | + * Can be composed |
| 70 | + * Are always resolved/rejected asynchronously |
| 71 | + * Are always multicast to multiple receivers |
| 72 | + |
| 73 | + Observables |
| 74 | + * Produce a number of values over time |
| 75 | + * Can be composed |
| 76 | + * Resolve synchronously/asynchronously |
| 77 | + * Multicast when needed |
| 78 | + |
| 79 | + The strength of Observables is producing and handling values over time, which is something a Promise wasn't designed to do. Observables also provide mechanisms |
| 80 | + for easy cancellation, retrying upon failure and transformations. Observables include a rich library of operators, along with the extensibility to provide a more powerful |
| 81 | + tool to handle the various streams of events in an application. So does this mean Promises are no longer needed? Absolutely not. Promises will continue to serve a purpose as the right tool for the job in some situations. |
| 82 | + The good news is Observables support conversion to a Promise for instances where a one-off value needs to be produced without managing the lifecycle |
| 83 | + of an Observable. |
| 84 | + |
| 85 | +:marked |
| 86 | + Observable Anatomy: Next, Error, and Complete |
| 87 | + |
| 88 | + Even though an Observable is a function that performs an action, it has a lifecycle. An Observable has 3 types of notifications it produces through its |
| 89 | + lifetime: `next`, `error`, and `complete`. |
| 90 | + |
| 91 | + The `next` is called whenever the observable produces a new value, notifying the Observer |
| 92 | + that some value was produced. The `next` Observables may produce a single value, or multiple values over time. These values can be a number, string, object |
| 93 | + |
| 94 | + |
| 95 | + Let's modify the HeroesReadyComponent to produce two values when subscribed. |
| 96 | + |
| 97 | + // Example of Observable that produces 2 values |
| 98 | + |
| 99 | + When click the `increment` button this time and subscribes to the Observable, the counter will be incremented twice and produce two values. |
| 100 | + |
| 101 | + Whenever an Observable produces an error, it uses the `error` event. An error event can only happen once during the invocation of an Observable action. |
| 102 | + |
| 103 | + // Example of Observable that errors |
| 104 | + |
| 105 | + The Observable notifies its Observer through that an error has occurred. |
| 106 | + |
| 107 | + Observables can also notify observers of completion. A completion event signals that no more values will be produced by the Observable. Like the error event, |
| 108 | + a completion event can only happen once. |
| 109 | + |
| 110 | + // Example of Observable that produces values, then completes |
| 111 | + |
| 112 | +:marked |
| 113 | + Observer: The Observable's Consumer |
| 114 | + An Observer is provided to an Observable to consume its values produced. An observer provides callbacks for the notification types produced by an Observable: `next`, |
| 115 | + `error`, and `complete`. |
| 116 | + |
| 117 | + An Observer is provided to an Observable through the `subscribe` method in 2 ways |
| 118 | + |
| 119 | + * As a single object containing 3 callbacks for each notification. |
| 120 | + * As 3 arguments in the subscribe method for each notification |
| 121 | + |
| 122 | + // Example of single object and logging out each notification |
| 123 | + |
| 124 | + // Example of 3 arguments to subscribe method |
| 125 | + |
| 126 | +:marked |
| 127 | + Subscription: Maintaining of resources |
| 128 | + |
| 129 | + As mentioned earlier, an Observable is not invoked until its `subscribed` to. This starts the execution of the Observable to produce values or events to an Observer. |
| 130 | + This subscription is an allocation of resources for the action performed by the Observable. Naturally, you want to clean up resources used by the Observable when |
| 131 | + finished with its execution. Each time an Observable is subscribed, it returns a `Subscription`. The `Subscription` is an object that handles the resources provided |
| 132 | + by the Observable, along with the `unsubscribe`. The `unsubscribe` method provided by the `Subscription` disposes of the resources allocated by the Observable. |
| 133 | + |
| 134 | + // Example of unsubscribing |
| 135 | + |
| 136 | + As a general rule and good practice, resources that are allocated and used must be cleaned up, and the Observable subscription is no different. Angular provides |
| 137 | + APIs that manage Observable lifecycles without the need to unsubscribe, but for those Observables you create, cleanup of resources is a must to protect against |
| 138 | + memory leaks and other unwanted side effects. |
| 139 | + |
| 140 | + // Example of unsubscribe in ngOnDestroy |
| 141 | + |
| 142 | + In this example, only one subscription is used and disposed of. Managing multiple subscriptions using the `unsubscribe` method |
| 143 | + has the potential to get unwieldy. You'll learn about different ways to to end subscriptions of multiple Observables later in the chapter. |
| 144 | + |
| 145 | + // Example of unsubscribe using a Subject |
| 146 | + |
| 147 | +:marked |
| 148 | + Operators: Observable functions |
| 149 | + |
| 150 | + The Observable prototype contains many `operators`, which are methods that perform an operation. Operators are **pure functions** |
| 151 | + that are stateless. Stateless operators are less error-prone as they are provided an Observable, in which an operation is performed on, |
| 152 | + and return a new Observable, without modifying the original Observable. The Observable prototype comes with a minimal set of operators by default, |
| 153 | + with a large set of operators that can be added. Operators are also used to (create Observable instances) from existing events. |
| 154 | + |
| 155 | + There are different ways to access the operators provided by RxJS: By patching the Observable prototype or by importing the operator functions directly. |
| 156 | + Each operator only needs to be patched on the prototype once, but where you choose to patch the Observable requires consideration. We'll examine a |
| 157 | + few options below: |
| 158 | + |
| 159 | + // Example of importing entire rxjs/Rx library |
| 160 | + |
| 161 | + By importing `rxjs/Rx`, the **entire** set of RxJS operators are added to the Observable prototype. In most cases, you will only use a subset of |
| 162 | + the available operators and adding the entire set increases the overall bundle size of your application. This is |
| 163 | + only recommended for sample apps, and in testing environments. |
| 164 | + |
| 165 | + // Example of patching the Observable prototype |
| 166 | + |
| 167 | + Since minimizing bundle size is a recommended practice, you should only import the operators you need, where you need them. |
| 168 | + This approach includes importing operators multiple times in different files, but safeguards against using operators |
| 169 | + without having patched the Observable prototype first. Since feature areas can be loaded lazily, this also allows you the benefit |
| 170 | + of keeping certain operators in separate bundles and only loaded them when needed. |
| 171 | + |
| 172 | + // Example of importing operators directly |
| 173 | + |
| 174 | + // Note about tree-shaking when pat |
| 175 | + |
| 176 | + NOTE: Operators patched onto the Observable prototype that are unused will not be tree-shaken. |
| 177 | + |
| 178 | + Operators can also be directly imported and used without patching the Observable prototype. This is the |
| 179 | + recommended option for third-party libraries, staying with the approach to only import what you need. It |
| 180 | + is also more suited for tree-shaking when bundling your Angular application. |
| 181 | + |
| 182 | +:marked |
0 commit comments