diff --git a/apps/rxjs.dev/content/guide/observable.md b/apps/rxjs.dev/content/guide/observable.md index 71f83120de..d2fc0bafb1 100644 --- a/apps/rxjs.dev/content/guide/observable.md +++ b/apps/rxjs.dev/content/guide/observable.md @@ -12,7 +12,7 @@ Observables are lazy Push collections of multiple values. They fill the missing ```ts import { Observable } from 'rxjs'; -const observable = new Observable((subscriber) => { +const observable = new Observable((subscriber) => { subscriber.next(1); subscriber.next(2); subscriber.next(3); @@ -28,7 +28,7 @@ To invoke the Observable and see these values, we need to _subscribe_ to it: ```ts import { Observable } from 'rxjs'; -const observable = new Observable((subscriber) => { +const observable = new Observable((subscriber) => { subscriber.next(1); subscriber.next(2); subscriber.next(3); @@ -127,7 +127,7 @@ You can write the same behavior above, but with Observables: ```ts import { Observable } from 'rxjs'; -const foo = new Observable((subscriber) => { +const foo = new Observable((subscriber) => { console.log('Hello'); subscriber.next(42); }); @@ -208,7 +208,7 @@ Functions can only return one value. Observables, however, can do this: ```ts import { Observable } from 'rxjs'; -const foo = new Observable((subscriber) => { +const foo = new Observable((subscriber) => { console.log('Hello'); subscriber.next(42); subscriber.next(100); // "return" another value @@ -238,7 +238,7 @@ But you can also "return" values asynchronously: ```ts import { Observable } from 'rxjs'; -const foo = new Observable((subscriber) => { +const foo = new Observable((subscriber) => { console.log('Hello'); subscriber.next(42); subscriber.next(100); @@ -292,7 +292,7 @@ The following example creates an Observable to emit the string `'hi'` every seco ```ts import { Observable } from 'rxjs'; -const observable = new Observable(function subscribe(subscriber) { +const observable = new Observable(function subscribe(subscriber) { const id = setInterval(() => { subscriber.next('hi'); }, 1000); @@ -311,9 +311,9 @@ The Observable `observable` in the example can be _subscribed_ to, like this: observable.subscribe((x) => console.log(x)); ``` -It is not a coincidence that `observable.subscribe` and `subscribe` in `new Observable(function subscribe(subscriber) {...})` have the same name. In the library, they are different, but for practical purposes you can consider them conceptually equal. +It is not a coincidence that `observable.subscribe` and `subscribe` in `new Observable(function subscribe(subscriber) {...})` have the same name. In the library, they are different, but for practical purposes you can consider them conceptually equal. -This shows how `subscribe` calls are not shared among multiple Observers of the same Observable. When calling `observable.subscribe` with an Observer, the function `subscribe` in `new Observable(function subscribe(subscriber) {...})` is run for that given subscriber. Each call to `observable.subscribe` triggers its own independent setup for that given subscriber. +This shows how `subscribe` calls are not shared among multiple Observers of the same Observable. When calling `observable.subscribe` with an Observer, the function `subscribe` in `new Observable(function subscribe(subscriber) {...})` is run for that given subscriber. Each call to `observable.subscribe` triggers its own independent setup for that given subscriber. Subscribing to an Observable is like calling a function, providing callbacks where the data will be delivered to. @@ -323,7 +323,7 @@ A `subscribe` call is simply a way to start an "Observable execution" and delive ### Executing Observables -The code inside `new Observable(function subscribe(subscriber) {...})` represents an "Observable execution", a lazy computation that only happens for each Observer that subscribes. The execution produces multiple values over time, either synchronously or asynchronously. +The code inside `new Observable(function subscribe(subscriber) {...})` represents an "Observable execution", a lazy computation that only happens for each Observer that subscribes. The execution produces multiple values over time, either synchronously or asynchronously. There are three types of values an Observable Execution can deliver: @@ -346,7 +346,7 @@ The following is an example of an Observable execution that delivers three Next ```ts import { Observable } from 'rxjs'; -const observable = new Observable(function subscribe(subscriber) { +const observable = new Observable(function subscribe(subscriber) { subscriber.next(1); subscriber.next(2); subscriber.next(3); @@ -359,7 +359,7 @@ Observables strictly adhere to the Observable Contract, so the following code wo ```ts import { Observable } from 'rxjs'; -const observable = new Observable(function subscribe(subscriber) { +const observable = new Observable(function subscribe(subscriber) { subscriber.next(1); subscriber.next(2); subscriber.next(3); @@ -373,7 +373,7 @@ It is a good idea to wrap any code in `subscribe` with `try`/`catch` block that ```ts import { Observable } from 'rxjs'; -const observable = new Observable(function subscribe(subscriber) { +const observable = new Observable(function subscribe(subscriber) { try { subscriber.next(1); subscriber.next(2); @@ -415,7 +415,7 @@ For instance, this is how we clear an interval execution set with `setInterval`: ```ts import { Observable } from 'rxjs'; -const observable = new Observable(function subscribe(subscriber) { +const observable = new Observable(function subscribe(subscriber) { // Keep track of the interval resource const intervalId = setInterval(() => { subscriber.next('hi'); @@ -428,7 +428,7 @@ const observable = new Observable(function subscribe(subscriber) { }); ``` -Just like `observable.subscribe` resembles `new Observable(function subscribe() {...})`, the `unsubscribe` we return from `subscribe` is conceptually equal to `subscription.unsubscribe`. In fact, if we remove the ReactiveX types surrounding these concepts, we're left with rather straightforward JavaScript. +Just like `observable.subscribe` resembles `new Observable(function subscribe() {...})`, the `unsubscribe` we return from `subscribe` is conceptually equal to `subscription.unsubscribe`. In fact, if we remove the ReactiveX types surrounding these concepts, we're left with rather straightforward JavaScript. ```ts function subscribe(subscriber) { diff --git a/apps/rxjs.dev/content/guide/subject.md b/apps/rxjs.dev/content/guide/subject.md index 2938b633d9..33546a2faf 100644 --- a/apps/rxjs.dev/content/guide/subject.md +++ b/apps/rxjs.dev/content/guide/subject.md @@ -71,14 +71,16 @@ A "multicasted Observable" passes notifications through a Subject which may have A multicasted Observable uses a Subject under the hood to make multiple Observers see the same Observable execution. -Under the hood, this is how the `multicast` operator works: Observers subscribe to an underlying Subject, and the Subject subscribes to the source Observable. The following example is similar to the previous example which used `observable.subscribe(subject)`: +Under the hood, this is how the `connectable` operator works: Observers subscribe to an underlying Subject, and the Subject subscribes to the source Observable. The following example is similar to the previous example which used `observable.subscribe(subject)`: ```ts -import { from, Subject, multicast } from 'rxjs'; +import { connectable, from, Subject } from 'rxjs'; const source = from([1, 2, 3]); -const subject = new Subject(); -const multicasted = source.pipe(multicast(subject)); +const subjectFactory = () => new Subject(); +const multicasted = connectable(source, { + connector: subjectFactory, +}); // These are, under the hood, `subject.subscribe({...})`: multicasted.subscribe({ @@ -92,7 +94,7 @@ multicasted.subscribe({ multicasted.connect(); ``` -`multicast` returns an Observable that looks like a normal Observable, but works like a Subject when it comes to subscribing. `multicast` returns a `ConnectableObservable`, which is simply an Observable with the `connect()` method. +`connectable` returns an Observable that looks like a normal Observable, but works like a Subject when it comes to subscribing. `connectable` returns a `ConnectableObservable`, which is simply an Observable with the `connect()` method. The `connect()` method is important to determine exactly when the shared Observable execution will start. Because `connect()` does `source.subscribe(subject)` under the hood, `connect()` returns a Subscription, which you can unsubscribe from in order to cancel the shared Observable execution. @@ -116,12 +118,14 @@ Consider the following example where subscriptions occur as outlined by this lis To achieve that with explicit calls to `connect()`, we write the following code: ```ts -import { interval, Subject, multicast } from 'rxjs'; +import { connectable, interval, Subject, Subscription } from 'rxjs'; const source = interval(500); -const subject = new Subject(); -const multicasted = source.pipe(multicast(subject)); -let subscription1, subscription2, subscriptionConnect; +const subjectFactory = () => new Subject(); +const multicasted = connectable(source, { + connector: subjectFactory, +}); +let subscription1, subscription2: Subscription, subscriptionConnect; subscription1 = multicasted.subscribe({ next: (v) => console.log(`observerA: ${v}`), @@ -155,12 +159,12 @@ If we wish to avoid explicit calls to `connect()`, we can use ConnectableObserva Below is an example: ```ts -import { interval, Subject, multicast, refCount } from 'rxjs'; +import { interval, Subject, Subscription, multicast, refCount } from 'rxjs'; const source = interval(500); -const subject = new Subject(); +const subject = new Subject(); const refCounted = source.pipe(multicast(subject), refCount()); -let subscription1, subscription2; +let subscription1, subscription2: Subscription; // This calls `connect()`, because // it is the first subscriber to `refCounted` @@ -211,10 +215,10 @@ In the following example, the BehaviorSubject is initialized with the value `0` ```ts import { BehaviorSubject } from 'rxjs'; -const subject = new BehaviorSubject(0); // 0 is the initial value +const subject = new BehaviorSubject(0); // 0 is the initial value - and its type is inferred automatically subject.subscribe({ - next: (v) => console.log(`observerA: ${v}`), + next: (v: number) => console.log(`observerA: ${v}`), }); subject.next(1); @@ -245,10 +249,10 @@ When creating a `ReplaySubject`, you can specify how many values to replay: ```ts import { ReplaySubject } from 'rxjs'; -const subject = new ReplaySubject(3); // buffer 3 values for new subscribers +const subject = new ReplaySubject(3); // buffer 3 values for new subscribers subject.subscribe({ - next: (v) => console.log(`observerA: ${v}`), + next: (v: number) => console.log(`observerA: ${v}`), }); subject.next(1); @@ -280,10 +284,10 @@ You can also specify a _window time_ in milliseconds, besides of the buffer size ```ts import { ReplaySubject } from 'rxjs'; -const subject = new ReplaySubject(100, 500 /* windowTime */); +const subject = new ReplaySubject(100, 500 /* windowTime */); subject.subscribe({ - next: (v) => console.log(`observerA: ${v}`), + next: (v: number) => console.log(`observerA: ${v}`), }); let i = 1; @@ -313,9 +317,9 @@ setTimeout(() => { The AsyncSubject is a variant where only the last value of the Observable execution is sent to its observers, and only when the execution completes. -```js +```ts import { AsyncSubject } from 'rxjs'; -const subject = new AsyncSubject(); +const subject = new AsyncSubject(); subject.subscribe({ next: (v) => console.log(`observerA: ${v}`), @@ -355,17 +359,10 @@ Passing a dummy value this way is clumsy and can confuse users. By declaring a _void subject_, you signal that the value is irrelevant. Only the event itself matters. -```ts -const subject = new Subject(); -setTimeout(() => subject.next(), 1000); -``` - -A complete example with context is shown below: - ```ts import { Subject } from 'rxjs'; -const subject = new Subject(); // Shorthand for Subject +const subject = new Subject(); subject.subscribe({ next: () => console.log('One second has passed'), @@ -374,4 +371,4 @@ subject.subscribe({ setTimeout(() => subject.next(), 1000); ``` -Before version 7, the default type of Subject values was `any`. `Subject` disables type checking of the emitted values, whereas `Subject` prevents accidental access to the emitted value. If you want the old behavior, then replace `Subject` with `Subject`. +Before version 7, the default type of Subject values was `any`. In v7+, the default type is `unknown`. `Subject` disables type checking of the emitted values, whereas `Subject` forces strict type-checking. Specifying an explicit type instead of the default is generally recommended.