None of the guide pages mentions ngModules. Only `observables-in-angular` needed conversion to Standalone. However, some of the guide pages reflect old versions of RxJS, including signatures that are no longer valid. These have been corrected. More significantly, *the existing guide is pretty bad at explaining RxJS and its usage*. It was written (by me I think) in the very early days of Angular and Angular RxJS instruction. I've taught numerous "RxJS in Angular" classes since and learned from that experience what does and does not work with students. There was neither the time nor the charter to completely overhaul this guide. But this commit attempts to remove what flops with students and to bring the teaching closer to what seems more effectively. I hope reviewers agree that my revisions are an improvement. **Revised Overview** The overview doc, `observables.md`, had a few errors (ex: `next` is NOT REQUIRED) and deprecated patterns (you now must pass the Observer object to `subscribe`). More importantly, it was wildly overcomplicated and scary, especially when it got into multi-casting. Moved the multi-casting section to "RxJS Library" and rewrote it (with working example) for simplicity and context. I made other changes in an effort to make this an overview that is more comprehensive and more clear. I paid particular attention to the "Basic usage and terms" section. Finally, I relocated the "Naming conventions for observables" section here from `rx-library`. This is the section that describes the dollar-sign convention. It made more sense for it to be here. **Revised "RxJS Library" page and code** *RxJS no longer supports chaining* and hasn't for a very long time. Removed note in `rx-library.md` that suggested you could use operator chaining. The RxJS `pipe` discussion in the "Operators" section was just weird. Almost no one calls the `pipe` function. We certainly should *start* there. We should start with how people actually use operators - by adding them to the argument list of the `Observable.pipe()` method. I kept the original `pipe` function example but subordinated it in a "callout". Most readers will (and should) ignore it. `Subject` is a *critically important RxJS mechanism for creating custom observables*. It was completely missing from the list of observable creators on this guide page. So I added it to the "Observable creation functions" section of the guide and wrote an accompanying `MessageService` code sample (see the new `rx-library/app/` folder). The `MessageService` is a pretty common pattern in Angular apps - far more common than creating an observable from a counter or an event, two of the creation patterns currently on this page. This new section also afforded an opportunity to show how RxJS helps with building loosely coupled applications. We will soon be talking about Signals. Many will wonder whether and when they should still use RxJS. At least a partial answer is that RxJS is really good at progressively combining and enhancing streams of data as they cross component boundaries. Of course you can pass signals around; but they are not as rich in transformers as RxJS. This is where RxJS shines. **Revised "Comparing observables"** The Promises section in `comparing-observables.md` had many errors and misleading remarks. The comparison of error handling was especially egregious; the code example for that was nonsense. The "Chain" sub-section was really about transforming values. It also failed to demonstrate chaining promise `.then`s. Reworked these sub-sections and improved the code samples to match. PR Close #51516
7.9 KiB
Using observables for streams of values
Observables are a technique for event handling, asynchronous programming, and handling multiple values emitted over time.
The observer pattern is a software design pattern in which an object, called the subject, maintains a list of its dependents, called observers, and notifies them automatically of state changes.
This pattern is similar but not identical to the publish/subscribe design pattern.
Angular apps tend to use the RxJS library for Observables. This overview covers just the basics of observables as implemented by that library.
Basic usage and terms
Observables are declarative. You define a function for publishing values — the source — but that function is not executed until a consumer subscribes to the observable by calling the observable's subscribe method.
This subscriber then receives notifications from the observable until it completes, emits an error, or the consumer unsubscribes.
An observable can deliver multiple values of any type — literals, messages, or events — depending on the context. A stream of keystrokes, an HTTP response, and the ticks of an interval timer are among the typical observable sources. The observable API applies consistently across all of these diverse sources.
An observable can emit one, many, or no values while subscribed. It can emit synchronously (emit the first value immediately) or asynchronously (emit values over time).
Because setup and teardown logic are both handled by the observable, your application code only needs to worry about subscribing to consume values and unsubscribing when done.
RxJS Operators enable transformations of observable values. An Operator takes an observable source, manipulates the values from that source in some useful way, and returns a new observable of the transformed values. When you subscribe to that new observable, you get the results of the intermediate transformations.
This ability to progressively transform observable values - and even combine multiple observable sources into a consolidated observable - is one of the most powerful and appealing of RxJS features.
Accordingly, observables are used extensively within Angular applications and within Angular itself.
To be fair, RxJS has a steep learning curve and sometimes bewildering behavior. Use them judiciously.
Observable
An observable is an object that can emit one or more values over time.
Here's a simple observable that will emit 1, then 2, then 3, and then completes.
The RxJS method, of(...values), creates an Observable instance that synchronously delivers each of the values provided as arguments.
Naming conventions for observables
Notice the "$" on the end of the observable name. The "$" signifies that the variable is an observable "$tream" of values.
This is a widely adopted naming convention for observables.
Not everyone likes it. Because Angular applications are written in TypeScript and code editors are good at revealing an object's type, you can usually tell when a variable is an observable. Many feel the "$" suffix is unnecessary and potentially misleading.
On the other hand, the trailing "$" can help you quickly identify observables when scanning the code. Also, if you want a property to hold the most recent value emitted from an observable, it can be convenient to use the source observable's root name without the "$".
The Angular framework and tooling do not enforce this convention. Feel free to use it or not.
Subscribing
An observable begins publishing values only when someone subscribes to it. That "1-2-3" observable won't emit any numbers until you subscribe by calling the observable's subscribe() method.
If you want to begin publishing but don't care about the values or when it completes, you can call subscribe with no arguments at all
You're more likely interested in doing something with the values. Pass in a method - called a "next" handler - that does something every time the observable emits a value.
Passing a next() function into subcribe is a convenient syntax for this most typical case. If you also need to know when the observable emits an error or completes, you'll have to pass in an Observer instead.
Defining observers
An observable has three types of notifications: "next", "error", and "complete".
An Observer is an object whose properties contain handlers for these notifications.
| Notification type | Details |
|---|---|
next |
A handler for each delivered value. Called zero or more times after execution starts. |
error |
A handler for an error notification. An error halts execution of the observable instance and unsubscribes. |
complete |
A handler for the execution-complete notification. Do not expect next or error to be called again. Automatically unsubscribes. |
Here is an example of passing an observer object to subscribe:
Alternatively, you can create the Observer object with functions named next(), error() and complete().
This works because JavaScript turns the function names into the property names.
All of the handler properties are optional. If you omit a handler for one of these properties, the observer ignores notifications of that type.
Error handling
Because observables can produce values asynchronously, try/catch will not effectively catch errors.
Instead, you handle errors by specifying an error function on the observer.
Producing an error also causes the observable to clean up subscriptions and stop producing values.
Error handling and specifically recovering from an error is covered in more detail in a later section.
Creating observables
The RxJS library contains a number of functions for creating observables. Some of the most useful are covered later.
You can also use the Observable constructor to create an observable stream of any type.
The constructor takes as its argument the subscriber function to run when the observable's subscribe() method executes.
A subscriber function receives an Observer object, and can publish values to the observer's next(), error, and complete handlers.
For example, to create an observable equivalent to the of(1, 2, 3) above, you could write something like this:
Geolocation example
The following example demonstrates the concepts above by showing how to create and consume an observable that reports geolocation updates.
@reviewed 2023-08-25