5 lines
14 KiB
JSON
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{
"id": "guide/rx-library",
"title": "The RxJS library",
"contents": "\n\n\n<div class=\"github-links\">\n <a href=\"https://github.com/angular/angular/edit/master/aio/content/guide/rx-library.md?message=docs%3A%20describe%20your%20change...\" aria-label=\"Suggest Edits\" title=\"Suggest Edits\"><i class=\"material-icons\" aria-hidden=\"true\" role=\"img\">mode_edit</i></a>\n</div>\n\n\n<div class=\"content\">\n <h1 id=\"the-rxjs-library\">The RxJS library<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"guide/rx-library#the-rxjs-library\"><i class=\"material-icons\">link</i></a></h1>\n<p>Reactive programming is an asynchronous programming paradigm concerned with data streams and the propagation of change (<a href=\"https://en.wikipedia.org/wiki/Reactive_programming\">Wikipedia</a>). RxJS (Reactive Extensions for JavaScript) is a library for reactive programming using observables that makes it easier to compose asynchronous or callback-based code. See (<a href=\"https://rxjs.dev/guide/overview\">RxJS Docs</a>).</p>\n<p>RxJS provides an implementation of the <code>Observable</code> type, which is needed until the type becomes part of the language and until browsers support it. The library also provides utility functions for creating and working with observables. These utility functions can be used for:</p>\n<ul>\n<li>Converting existing code for async operations into observables</li>\n<li>Iterating through the values in a stream</li>\n<li>Mapping values to different types</li>\n<li>Filtering streams</li>\n<li>Composing multiple streams</li>\n</ul>\n<h2 id=\"observable-creation-functions\">Observable creation functions<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"guide/rx-library#observable-creation-functions\"><i class=\"material-icons\">link</i></a></h2>\n<p>RxJS offers a number of functions that can be used to create new observables. These functions can simplify the process of creating observables from things such as events, timers, promises, and so on. For example:</p>\n<code-example path=\"rx-library/src/simple-creation.1.ts\" region=\"promise\" header=\"Create an observable from a promise\">\nimport { from } from 'rxjs';\n\n// Create an Observable out of a promise\nconst data = from(fetch('/api/endpoint'));\n// Subscribe to begin listening for async result\ndata.subscribe({\n next(response) { console.log(response); },\n error(err) { console.error('Error: ' + err); },\n complete() { console.log('Completed'); }\n});\n\n\n</code-example>\n<code-example path=\"rx-library/src/simple-creation.2.ts\" region=\"interval\" header=\"Create an observable from a counter\">\nimport { interval } from 'rxjs';\n\n// Create an Observable that will publish a value on an interval\nconst secondsCounter = interval(1000);\n// Subscribe to begin publishing values\nconst subscription = secondsCounter.subscribe(n =>\n console.log(`It's been ${n + 1} seconds since subscribing!`));\n\n\n</code-example>\n<code-example path=\"rx-library/src/simple-creation.3.ts\" region=\"event\" header=\"Create an observable from an event\">\nimport { fromEvent } from 'rxjs';\n\nconst el = document.getElementById('my-element');\n\n// Create an Observable that will publish mouse movements\nconst mouseMoves = fromEvent(el, 'mousemove');\n\n// Subscribe to start listening for mouse-move events\nconst subscription = mouseMoves.subscribe((evt: MouseEvent) => {\n // Log coords of mouse movements\n console.log(`Coords: ${evt.clientX} X ${evt.clientY}`);\n\n // When the mouse is over the upper-left of the screen,\n // unsubscribe to stop listening for mouse movements\n if (evt.clientX &#x3C; 40 &#x26;&#x26; evt.clientY &#x3C; 40) {\n subscription.unsubscribe();\n }\n});\n\n\n</code-example>\n<code-example path=\"rx-library/src/simple-creation.ts\" region=\"ajax\" header=\"Create an observable that creates an AJAX request\">\n import { ajax } from 'rxjs/ajax';\n\n// Create an Observable that will create an AJAX request\n const apiData = ajax('/api/data');\n // Subscribe to create the request\n apiData.subscribe(res => console.log(res.status, res.response));\n\n</code-example>\n<h2 id=\"operators\">Operators<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"guide/rx-library#operators\"><i class=\"material-icons\">link</i></a></h2>\n<p>Operators are functions that build on the observables foundation to enable sophisticated manipulation of collections. For example, RxJS defines operators such as <code>map()</code>, <code>filter()</code>, <code>concat()</code>, and <code>flatMap()</code>.</p>\n<p>Operators take configuration options, and they return a function that takes a source observable. When executing this returned function, the operator observes the source observables emitted values, transforms them, and returns a new observable of those transformed values. Here is a simple example:</p>\n<code-example path=\"rx-library/src/operators.ts\" header=\"Map operator\">\nimport { of } from 'rxjs';\nimport { map } from 'rxjs/operators';\n\nconst nums = of(1, 2, 3);\n\nconst squareValues = map((val: number) => val * val);\nconst squaredNums = squareValues(nums);\n\nsquaredNums.subscribe(x => console.log(x));\n\n// Logs\n// 1\n// 4\n// 9\n\n\n</code-example>\n<p>You can use <em>pipes</em> to link operators together. Pipes let you combine multiple functions into a single function. The <code>pipe()</code> function takes as its arguments the functions you want to combine, and returns a new function that, when executed, runs the composed functions in sequence.</p>\n<p>A set of operators applied to an observable is a recipe—that is, a set of instructions for producing the values youre interested in. By itself, the recipe doesnt do anything. You need to call <code>subscribe()</code> to produce a result through the recipe.</p>\n<p>Heres an example:</p>\n<code-example path=\"rx-library/src/operators.1.ts\" header=\"Standalone pipe function\">\nimport { of, pipe } from 'rxjs';\nimport { filter, map } from 'rxjs/operators';\n\nconst nums = of(1, 2, 3, 4, 5);\n\n// Create a function that accepts an Observable.\nconst squareOddVals = pipe(\n filter((n: number) => n % 2 !== 0),\n map(n => n * n)\n);\n\n// Create an Observable that will run the filter and map functions\nconst squareOdd = squareOddVals(nums);\n\n// Subscribe to run the combined functions\nsquareOdd.subscribe(x => console.log(x));\n\n\n</code-example>\n<p>The <code>pipe()</code> function is also a method on the RxJS <code>Observable</code>, so you use this shorter form to define the same operation:</p>\n<code-example path=\"rx-library/src/operators.2.ts\" header=\"Observable.pipe function\">\nimport { of } from 'rxjs';\nimport { filter, map } from 'rxjs/operators';\n\nconst squareOdd = of(1, 2, 3, 4, 5)\n .pipe(\n filter(n => n % 2 !== 0),\n map(n => n * n)\n );\n\n// Subscribe to get values\nsquareOdd.subscribe(x => console.log(x));\n\n\n</code-example>\n<h3 id=\"common-operators\">Common operators<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"guide/rx-library#common-operators\"><i class=\"material-icons\">link</i></a></h3>\n<p>RxJS provides many operators, but only a handful are used frequently. For a list of operators and usage samples, visit the <a href=\"https://rxjs.dev/api\">RxJS API Documentation</a>.</p>\n<div class=\"alert is-helpful\">\n Note that, for Angular apps, we prefer combining operators with pipes, rather than chaining. Chaining is used in many RxJS examples.\n</div>\n<table>\n<thead>\n<tr>\n<th align=\"left\">Area</th>\n<th align=\"left\">Operators</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td align=\"left\">Creation</td>\n<td align=\"left\"><code>from</code>, <code>fromEvent</code>, <code>of</code></td>\n</tr>\n<tr>\n<td align=\"left\">Combination</td>\n<td align=\"left\"><code>combineLatest</code>, <code>concat</code>, <code>merge</code>, <code>startWith</code> , <code>withLatestFrom</code>, <code>zip</code></td>\n</tr>\n<tr>\n<td align=\"left\">Filtering</td>\n<td align=\"left\"><code>debounceTime</code>, <code>distinctUntilChanged</code>, <code>filter</code>, <code>take</code>, <code>takeUntil</code></td>\n</tr>\n<tr>\n<td align=\"left\">Transformation</td>\n<td align=\"left\"><code>bufferTime</code>, <code>concatMap</code>, <code>map</code>, <code>mergeMap</code>, <code>scan</code>, <code>switchMap</code></td>\n</tr>\n<tr>\n<td align=\"left\">Utility</td>\n<td align=\"left\"><code>tap</code></td>\n</tr>\n<tr>\n<td align=\"left\">Multicasting</td>\n<td align=\"left\"><code>share</code></td>\n</tr>\n</tbody>\n</table>\n<h2 id=\"error-handling\">Error handling<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"guide/rx-library#error-handling\"><i class=\"material-icons\">link</i></a></h2>\n<p>In addition to the <code>error()</code> handler that you provide on subscription, RxJS provides the <code>catchError</code> operator that lets you handle known errors in the observable recipe.</p>\n<p>For instance, suppose you have an observable that makes an API request and maps to the response from the server. If the server returns an error or the value doesnt exist, an error is produced. If you catch this error and supply a default value, your stream continues to process values rather than erroring out.</p>\n<p>Here's an example of using the <code>catchError</code> operator to do this:</p>\n<code-example path=\"rx-library/src/error-handling.ts\" header=\"catchError operator\">\nimport { of } from 'rxjs';\nimport { ajax } from 'rxjs/ajax';\nimport { map, catchError } from 'rxjs/operators';\n\n// Return \"response\" from the API. If an error happens,\n// return an empty array.\nconst apiData = ajax('/api/data').pipe(\n map((res: any) => {\n if (!res.response) {\n throw new Error('Value expected!');\n }\n return res.response;\n }),\n catchError(err => of([]))\n);\n\napiData.subscribe({\n next(x) { console.log('data: ', x); },\n error(err) { console.log('errors already caught... will not run'); }\n});\n\n\n</code-example>\n<h3 id=\"retry-failed-observable\">Retry failed observable<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"guide/rx-library#retry-failed-observable\"><i class=\"material-icons\">link</i></a></h3>\n<p>Where the <code>catchError</code> operator provides a simple path of recovery, the <code>retry</code> operator lets you retry a failed request.</p>\n<p>Use the <code>retry</code> operator before the <code>catchError</code> operator. It resubscribes to the original source observable, which can then re-run the full sequence of actions that resulted in the error. If this includes an HTTP request, it will retry that HTTP request.</p>\n<p>The following converts the previous example to retry the request before catching the error:</p>\n<code-example path=\"rx-library/src/retry-on-error.ts\" header=\"retry operator\">\nimport { of } from 'rxjs';\nimport { ajax } from 'rxjs/ajax';\nimport { map, retry, catchError } from 'rxjs/operators';\n\nconst apiData = ajax('/api/data').pipe(\n map((res: any) => {\n if (!res.response) {\n console.log('Error occurred.');\n throw new Error('Value expected!');\n }\n return res.response;\n }),\n retry(3), // Retry up to 3 times before failing\n catchError(err => of([]))\n);\n\napiData.subscribe({\n next(x) { console.log('data: ', x); },\n error(err) { console.log('errors already caught... will not run'); }\n});\n\n\n</code-example>\n<div class=\"alert is-helpful\">\n<p> Do not retry <strong>authentication</strong> requests, since these should only be initiated by user action. We don't want to lock out user accounts with repeated login requests that the user has not initiated.</p>\n</div>\n<h2 id=\"naming-conventions-for-observables\">Naming conventions for observables<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"guide/rx-library#naming-conventions-for-observables\"><i class=\"material-icons\">link</i></a></h2>\n<p>Because Angular applications are mostly written in TypeScript, you will typically know when a variable is an observable. Although the Angular framework does not enforce a naming convention for observables, you will often see observables named with a trailing “$” sign.</p>\n<p>This can be useful when scanning through code and looking for observable values. Also, if you want a property to store the most recent value from an observable, it can be convenient to simply use the same name with or without the “$”.</p>\n<p>For example:</p>\n<code-example path=\"rx-library/src/naming-convention.ts\" header=\"Naming observables\">\n\n\nimport { <a href=\"api/core/Component\" class=\"code-anchor\">Component</a> } from '@angular/core';\nimport { Observable } from 'rxjs';\n\n@<a href=\"api/core/Component\" class=\"code-anchor\">Component</a>({\n selector: 'app-stopwatch',\n templateUrl: './stopwatch.component.html'\n})\nexport class StopwatchComponent {\n\n stopwatchValue: number;\n stopwatchValue$: Observable&#x3C;number>;\n\n start() {\n this.stopwatchValue$.subscribe(num =>\n this.stopwatchValue = num\n );\n }\n}\n\n\n</code-example>\n\n \n</div>\n\n<!-- links to this doc:\n - guide/http\n-->\n<!-- links from this doc:\n - api/core/Component\n - guide/rx-library#common-operators\n - guide/rx-library#error-handling\n - guide/rx-library#naming-conventions-for-observables\n - guide/rx-library#observable-creation-functions\n - guide/rx-library#operators\n - guide/rx-library#retry-failed-observable\n - guide/rx-library#the-rxjs-library\n - https://en.wikipedia.org/wiki/Reactive_programming\n - https://github.com/angular/angular/edit/master/aio/content/guide/rx-library.md?message=docs%3A%20describe%20your%20change...\n - https://rxjs.dev/api\n - https://rxjs.dev/guide/overview\n-->"
}