In RxJS 6, how do I import a static merge function for merging a list of Observables?
I want to be able to do:
const merged$ = merge(
obs1$,
obs2$,
obs3$
);
I've tried:
import { merge } from 'rxjs/observable/merge'; and
import { merge } from 'rxjs/operators';
but neither seems to give me what I want.
Importing has been made easy in RxJS 6:
import { merge } from 'rxjs';
You may want to read the official migration guide.
Another useful resource regarding importing in RxJS 6 is this talk by Ben Lesh who is the RxJS lead.
RxJS 7.X
In RxJS v7.X the merge() method is depricated and will be removed un RxJs v8.X, use mergeWith() instead.
See:
https://rxjs.dev/api/operators/mergeWith
https://rxjs.dev/api/operators/merge (depricated)
import { fromEvent } from 'rxjs';
import { map, mergeWith } from 'rxjs/operators';
const clicks$ = fromEvent(document, 'click').pipe(map(() => 'click'));
const mousemoves$ = fromEvent(document, 'mousemove').pipe(map(() => 'mousemove'));
const dblclicks$ = fromEvent(document, 'dblclick').pipe(map(() => 'dblclick'));
mousemoves$.pipe(
mergeWith(clicks$, dblclicks$),
)
.subscribe(x => console.log(x));
// result (assuming user interactions)
// "mousemove"
// "mousemove"
// "mousemove"
// "click"
// "click"
// "dblclick"
(example from api docs)
I believe now when the "creation" classes were removed the recommended way is importing directly from 'rxjs':
import { merge as mergeStatic } from 'rxjs';
Previous alpha version of RxJS 6 used to have 'rxjs/create' file but this has been removed already: https://github.com/ReactiveX/rxjs/blob/master/CHANGELOG.md#600-alpha3-2018-02-06
However this expects you to use path maps correctly otherwise you'll import a lot of things you don't need. If you don't use path maps or the build process hidden from you you can import directly the correct file:
import { merge as mergeStatic } from 'rxjs/internal/observable/merge';
As of RXJS 6. The merge is in the rxjs/operators
import { map, take, merge, switchMap, filter } from 'rxjs/operators';
Related
As part of learning rxjs ive been using create methods of, from, interval etc. to test throttle and deboucne etc ive been creating streams using fromevent.
now i have a real use case and i need to dynamically add values into an empty observable stream. i cant find any examples on how best to do this NOT using the creation methods above. Presently Im using a BehaviourSubject to dynamically add items to a stream using next(). Is this the best/preferred way of DYNAMICALLY adding new items to a stream?
e.g.
import { BehaviorSubject, timer } from 'rxjs';
import { tap, mapTo, concatMap, } from 'rxjs/operators';
const subject = new BehaviorSubject(1);
const example = subject.pipe(
concatMap(ev => timer(200).pipe(mapTo(ev))),
tap((ev) => console.log(ev))
)
example.subscribe();
// add a flurry of values dynamically
subject.next(2);
subject.next(3);
subject.next(4);
// some time later add some more
setTimeout(function(){
subject.next(5);
subject.next(6);
subject.next(7);
}, 5000);
https://stackblitz.com/edit/rxjs-behaviorsubject-simpleexample-gyrtw8?file=index.ts
Thanks
If you have a veeery custom logic of adding values that should be emitted in an Observable, you can create your own (instead of using fromEvent, of, from, ...):
const myObservable = new Observable(subscriber => {
subscriber.next(1);
subscriber.next(2);
subscriber.next(3);
setTimeout(() => {
subscriber.next(4);
subscriber.next(5);
setTimeout(() => {
subscriber.next(6);
}, 2000);
}, 1000);
});
However, the rxjs's creation functions should cover 99% of your needs.
The code above can be also written like:
concat(
of(1,2,3),
of(4,5).pipe(
delay(1000)
),
of(6).pipe(
delay(2000)
)
)
UPD: About Subjects
Subject is also an Observable so in your case using Subject is applicable but might not be the best option. The idea of a Subject is that there can be more than one subscriber (the one who uses the values from subject) but I'm not sure that it's your case (by the way - you can provide your real-life example to help us understand what you want to achieve)
Since RxJS v.6.5, the static combineLatest syntax
combined$ = combineLatest(a$,b$,c$); is deprecated.
Instead you should use following syntax:
combined$ = combineLatest([a$,b$,c$]);
Where they are: a$: Observable<T>, b$: Observable<U>, c$: Observable<V>
This declaration although gives me several linting errors:
Argument type [Observable<ObservableValueOf<Observable<T>>>,
Observable<ObservableValueOf<Observable<U>>>,
Observable<ObservableValueOf<Observable<V>>>] is not assignable to
parameter type [Observable<ObservableValueOf<Observable<T>>>]
So, where is my error?
Thanks a lot.
You should import combineLatest from rxjs instead of rxjs/operators like this:
import { of, combineLatest } from 'rxjs';
const a$ = of(true);
const b$ = of(false);
combineLatest([a$, b$]).pipe(
tap(console.log)
).subscribe();
Hope it helps.
I have legacy code I need to migrate to RxJS 6. There is one line that I have no clue how to migrate that one:
return empty(this.scheduler);
this.scheduler is some scheduler passed into the class constructor.
I found instructions on how to migrate empty(), just use the contant EMPTY, but how can I create an empty observable using a scheduler?
Update March 2019: There's an opened PR that adds emptyScheduled() method: https://github.com/ReactiveX/rxjs/pull/4595
The empty() Observable creation method is deprecated as well now and the recommended way is using EMPTY:
import { EMPTY } from 'rxjs';
EMPTY just emits the complete notification so it doesn't accept any scheduler.
EDIT:
import { asyncScheduler, Observable } from 'rxjs';
const asyncEmpty = scheduler => new Observable(observer => {
scheduler.schedule(() => observer.complete());
});
asyncEmpty(asyncScheduler).subscribe({ complete: () => console.log('completed')});
https://stackblitz.com/edit/rxjs-bwn1y7
I've got an observable which is not long lived (http request).
I'm using publishReplay(1) and refCount() so that when there an attempt to access it at the same time, it'll return the same value without triggering the http call again.
But if all the subscriptions are unsubscribed, I need to make some cleanup.
I can't use finalize because:
if I use it before publishReplay then it get closed once the http request is done
if I use it after refCount it'll be run as soon as one observable unsubscribe (instead of when all have unsubscribed)
So basically what I'd like would be to pass a callback to refCount and call that callback when the number of subscriptions reaches 0. But it doesn't work like that. Is there any way to be "warned" when all the subscribers have unsubscribed?
The simplest way I can think of right now would be to create a custom operator that'd pretty much extend refCount to add a callback.
Any better thoughts? I'm pretty sure that there's a better way of doing that.
Thanks!
I am not gonna lie, I was happy to find out I wasn't the only one looking for something like that. There is one another person.
I ended up doing something like that:
import { Observable } from 'rxjs';
export function tapTeardown(teardownLogic: () => void) {
return <T>(source: Observable<T>): Observable<T> =>
new Observable<T>((observer) => {
const subscription = source.subscribe(observer);
return () => {
subscription.unsubscribe();
teardownLogic();
};
});
}
And you use it like:
const augmented = connection.pipe(
tapTeardown(() => /* SOME TEARDOWN LOGIC */),
shareReplay({ bufferSize: 1, refCount: true }),
);
I've tried it and it seems to work correctly.
Here's how it's used:
import { of, timer } from 'rxjs';
import { map, publishReplay, take } from 'rxjs/operators';
import { refCountCb } from './refCountCb';
const source = timer(2000, 10000).pipe(
map(x => `Hello ${x}!`),
publishReplay(1),
refCountCb(() => console.log('MAIN CLOSED'))
);
source.pipe(take(1)).subscribe(x => console.log(x));
source.pipe(take(1)).subscribe(x => console.log(x));
Output:
Hello 0!
Hello 0!
MAIN CLOSED
I've built the custom refCountCb operator based on the source of refCount. It's basically just adding a callback so I won't copy paste the whole code here but it's available on the stackblitz.
Full demo: https://stackblitz.com/edit/rxjs-h7dbfc?file=index.ts
If you have any other idea please share it, I'd be glad to discover different solutions!
I'm trying to do websocket setup in an redux-observable epic, and i'm going with an approach similar to this guy: https://github.com/MichalZalecki/connect-rxjs-to-react/issues/1
However, it looks like my first stab at wiring things up isn't working, even though it looks the same as the guy above:
import 'rxjs';
import Observable from 'rxjs';
import * as scheduleActions from '../ducks/schedule';
export default function connectSocket(action$, store) {
return action$.ofType(scheduleActions.CANCEL_RSVP)
.map(action => {
new Observable(observer => {
// do websocket stuff here
observer.next('message text');
});
})
.map(text => {
console.log("xxxxxxxxxxxxx: ", text);
return scheduleActions.rsvpCancelled(1);
});
};
However, I'm getting a Object is not a constructor error:
=== UPDATE ===
Looks like the suggestion to destructure the { Observable } export worked!
Not the only issue is that text doesn't seem to cross over to the next method...
import 'rxjs';
import { Observable } from 'rxjs';
import * as scheduleActions from '../ducks/schedule';
export default function connectSocket(action$, store) {
return action$.ofType(scheduleActions.CANCEL_RSVP)
.map(action => {
new Observable(observer => {
// do websocket stuff here
observer.next('message text');
});
})
.map(text => {
console.log("xxxxxxxxxxxxx: ", text); // prints undefined
return scheduleActions.rsvpCancelled(1);
});
};
In RxJS v5, the Observable class is available as named export, not the default export.
import { Observable } from 'rxjs';
Importing from regular rxjs will also import all of RxJS (adding all operators to the Observable prototype). This is described in the docs here. If you'd prefer to be more explicit and only import Observable itself you can import it directly at rxjs/Observable:
import { Observable } from 'rxjs/Observable';
Separately, you have a couple issues with the way you're mapping your custom Observable.
First Issue
You're not actually returning it. hehe. You're missing a return statement (or you can remove the curly braces and use arrow function implicit returns).
Second Issue
The regular .map() operator does not do anything special when you return an Observable. If you want the custom Observable to be subscribed to and flattened you'll need to use an operator that does flattening of some kind.
The most common two are mergeMap (aka flatMap) or switchMap.
action$.ofType(scheduleActions.CANCEL_RSVP)
.mergeMap(action => {
return new Observable(observer => {
// do websocket stuff here
observer.next('message text');
});
})
Which operator you need depends on your desired behavior. If you're not yet familiar, you can check out the documentation on the various operators or jump straight to the mergeMap and switchMap docs.
If you're adventurous, RxJS v5 does have WebSocket support out of box you can try with Observable.webSocket(). It's not documented very well, but you could also take a look at the unit tests, and for simple read-only unidirectional streaming it's pretty self explanatory--provide the URL and subscribe. It's actually incredibly powerful, if you can figure out how to use it, that is. Supports bi-directional, multiplex aka complex multiple input/output channels through a single socket. We use it at Netflix for several internal tools with thousands of rps.
You can take a look at Demo. Visit at Create Custom Observable