Rxjs throttleTime - do we need to use asObservable? - javascript

I have been using these two (pretty identical methods) of calling throttleTime, but I am unsure if thery are actually identical or not.
this.scrollSubject$ = new Subject<Event>();
this.scrollSubscription$ = this.scrollSubject$
.asObservable()
.throttleTime(400, undefined, { trailing: true })
.subscribe(event => this.throttledScrollHandler(event));
The same as above, but with asObservable
this.scrollSubscription$ = this.scrollSubject$
.throttleTime(400, undefined, { trailing: true})
.subscribe( event => this.throttledScrollHandler(event));
I forgot to add the asObservable on my second one, but it behaved in the same way, which was a surprise. Are these two methods the same?
I'm using Observables in Angular, but I don't think this is Angular specific

The answer to the question in the title is: yes, if you want to ensure that the subject's next, error and complete methods cannot be called via the composed observable, you should use asObservable.
So the two snippets in the question are not the same.
Subject implements lift. This means that the observable returned from an operator is a Subject. So, unless asObservable is called, it will be possible for next, error or complete to be called on the composed observable.
const subject = new Rx.Subject();
const withoutAsObservable = subject
.do(value => console.log(value));
console.log("withoutAsObservable is a subject:", withoutAsObservable instanceof Rx.Subject);
const withAsObservable = subject
.asObservable()
.do(value => console.log(value));
console.log("withAsObservable is a subject:", withAsObservable instanceof Rx.Subject);
subject.subscribe(
value => console.log("subject value:", value),
error => console.log("subject error:", error)
);
withoutAsObservable.next("some value");
withoutAsObservable.error(new Error("some error"));
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://unpkg.com/rxjs#5/bundles/Rx.min.js"></script>
For more information on lift see this issue.
Regarding your comment on another answer:
I can call this.scrollSubscription$['next'](new Event('asdf'));, and throttledScrollHandler() will print out that event
Calling next on the Subscription calls the next method on the Subscriber that was created from the next function you passed to subscribe. It has nothing to so with the subject and is not equivalent to calling the subject's next method.
The implementation of subscribe calls toSubscriber and that subscriber is returned.
Subscriber extends Subscription, but it also has a next method. And that's what you are calling.

Related

Exception in observable pipe is suppressed

Suppose I want observable to emit value periodically until another observable emits. So I can use timer and takeUntil to achive that.
But then I want to process every emitted value and stop (error) emitting when some condition becomes true. So I write next piece of code:
const { timer, Subject } = 'rxjs';
const { takeUntil, map } = 'rxjs/operators';
const s = new Subject();
let count = 0;
function processItem(item) {
count++;
return count < 3;
}
const value = "MyValue";
timer(0, 1000).pipe(
takeUntil(s),
map(() => {
if (processItem(value)) {
console.log(`Processing done`);
return value;
} else {
s.next(true);
s.complete();
console.error(`Processing error`);
throw new Error(`Stop pipe`);
}
}),
)
Playground
But instead of getting error I have my Observable completed.
Only if I comment out takeUntil(s) operator, I get error.
Looks like when pipe operator completes, it's value is not emitted immediately, but remembered and emitted at the end of next "iteration" of the pipe, then replaced by new result and so on. And in my situation next iteration, when error should be emitted, is prevented by takeUntil. Question is am I right with that assumption, and if so, why rxjs is designed in that way?
First of all, each Rx chain can emit one error or one complete notification but never both. (See http://reactivex.io/documentation/contract.html section "The Contract Governing Notifications").
takeUntil operator emits complete when it's notification Observable (s in your case) emits any next notification. This means that when s emits the chain will be completed and you never receive any further error notifications.
The last thing and probably most confusing is that everything in RxJS happens synchronously unless you work with time (eg. delay operator) or you specifically use observeOn operator with an asynchronous scheduler. So when you call s.next(true) inside map this next notification is immediately propagated to takeUntil which completes the chain and as I mentioned above, you can receive one error or one complete notification but never both.
It looks like you don't even need to be using takeUntil because if you throw an error inside map it's automatically wrapped and sent further as an error notification (How to throw error from RxJS map operator (angular)) and the chain is disposed automatically so there's no point in trying to complete it after that with takeUntil.

Why observable source does not emit values when used in race (or merge) but emits when I manually subscribe to it

I have three observable sources in my code that emit values of the same type.
const setTitle$ = params$.do(
params => this.titleService.setTitle( `${params[1].appname} - ${this.pagename}` )
).switchMap(
() => Observable.of(true)
);
const openDocument$ = params$.switchMap(
params => this.openDocument(params[0].id)
);
const saveDocument$ = params$.switchMap(
params => this.saveDocument(params[0].id)
);
When i use them in race like this
setTitle$.race(
openDocument$,
saveDocument$
).subscribe();
works only setTitle and when i subscribe manually to another two sorces like
const openDocument$ = params$.switchMap(
params => this.openDocument(params[0].id)
).subscribe();
const saveDocument$ = params$.switchMap(
params => this.saveDocument(params[0].id)
).subscribe();
then they work too. Help me understand why it's going on and how to force to work all sources in race, merge, etc.
From the documentation, the .race() operator does this:
The observable to emit first is used.
That is why, you will only get ONE emission, because only one out of the three observables that emits first will get emitted.
What you are looking for is .forkJoin() or .combineLatest().
If you want all the observables to execute in parallel and wait for ALL of them to come back as one observables, use .forkJoin():
Observable
.forkJoin([...setTitle$, openDocument$, saveDocument$])
.subscribe(([setTitle, openDocument, saveDocument]) => {
//do something with your your results.
//all three observables must be completed. If any of it was not completed, the other 2 observables will wait for it
})
If you however wants to listen to every emission of all the observables regardless when they are emitted, use .combineLatest():
Observable
.combineLatest(setTitle$, openDocument$, saveDocument$)
.subscribe(([setTitle, openDocument, saveDocument]) => {
//do something with your your results.
// as long as any of the observables completed, it will be emitted here.
});
Problem was with shared params source.
const params$ = this.route.params.map(
routeParams => {
return {
id: <string>routeParams['id']
};
}
).combineLatest(
this.config.getConfig()
).share();
I have shared it with share operator. But in this article from the first comment to my question i found this:
When using multiple async pipes on streams with default values, the .share() operator might cause problems:
The share() will publish the first value of the stream on the first subscription. The first async pipe will trigger that subscription and get that initial value. The second async pipe however will subscribe after that value has already been emitted and therefore miss that value.
The solution for this problem is the .shareReplay(1) operator, which will keep track of the previous value of the stream. That way all the async pipes will get the last value.
I replaced share() with shareReplay(1) and all sources began emitting values.
const params$ = this.route.params.map(
routeParams => {
return {
id: <string>routeParams['id']
};
}
).combineLatest(
this.config.getConfig()
).shareReplay(1);
Thanks to everyone for help!

Is there any way to check if source is subscribed?

Just as the title says, in Angular 2, is there any way to check if source is already subscribed? Because I have to check it before using
this.subscription.unsubscribe();
This is my code:
this.Source = Rx.Observable.timer(startTime, 60000).timeInterval().pluck('interval');
this.Subscription = this.Source
.subscribe(data => { something }
and then I want to be sure that it is subscribed before calling unsubscribe()
It seems you can check whether the subscription is closed with
this.subscription.closed
indicate whether this Subscription has already been unsubscribed.
I had a similar case, I had a if condition with one optional subscribe:
if (languageIsSet) {
// do things...
} else {
this.langSub = this.langService.subscribe(lang => {
// do things...
});
}
If you want to call unsubscribe safely (you don't know if is subscribed or not), just initialize the subscription with EMPTY instance:
private langSub: Subscription = Subscription.EMPTY;
Now you can unsubscribe without errors.
You can check if Subject has observers because it has a public property observers.
With Observables you can't because they don't typically have arrays of observers. Only if you've multicasted them via a Subject with the multicast() operator for example.
Maybe if you could describe your use case in more detail I'll be able to give you better advice.
const source = ...;
let subscribed = false;
Rx.Observable.defer(() => {
subscribed = true;
return source.finally(() => { subscribed = false });
})
As I can see in your code you always create a subscription.
So if you created subscriptions object it means subscription exists and you can unsubscribe.
It Still a bit not clear why you need to check is any subsection exist
Btw. unsubscribe() method checking is subscription closed or not.
Subscription is closed if somebody called unsubscribe() or observable is compleated

How to create an observable within an observable in Angular2

I might be off on the process, but here goes:
I have an angular2 service. The source for the data of this service is going to be localstorage... later optionally updated when a DB call using http returns. Because I'll be wanting to update the data returned as the various sources come back, it appears I want to use an observables. For now, I'm just trying to get the concept down, so I've skipped the localstorage aspect... but I'm including the 'backstory' so it makes (some) sense as to why I'm wanting to do this in multiple methods.
My thought was I would have a "getHTTPEvents()" method that would return an observable with the payload being the events from the DB. (the theory being that at some point in the future I'd also have a 'getLSEvents()' method that would piggy back in there)
To mock that up, I have this code:
private eventsUrl = 'app/mock-events.json';
getHTTPEvents() : Observable<Array<any>> {
return this._http.get(this.eventsUrl)
.map(response => response.json()['events'])
.catch(this.handleError); // handle error is a logging method
}
My goal would be to create a method that allows filtering on the returned events yet still returns an observable to users of the service. That is where my problem is. With that goal, I have a public method which will be called by users of the service. (attempted to use pattern from here https://coryrylan.com/blog/angular-2-observable-data-services)
public getEvents(key:string,value:string) : Observable<Array<any>> {
var allEventsObserve : Observable<Array<any>> = this.getHTTPEvents();
var filteredEventsObserve : Observable<Array<any>>;
allEventsObserve
.subscribe(
events => {
for(var i=0;i<events.length;i++) {
if(events[i][key]==value) {
console.log('MATCH!!!' + events[i][key]); // THIS WORKS!
return new Observable(observer => filteredEventsObserve = observer); // what do I need to return here? I want to return an observable so the service consumer can get updates
}
}
return allEventsObserve
},
error => console.error("Error retrieving all events for filtering: " + error));
}
The above doesn't work. I've watch lots of videos and read lots of tutorials about observables, but nothing I can find seems to go more indepth other than creating and using the http observable.
I further tried this method of making the new observable:
var newObs = Observable.create(function (observer) {
observer.next(events[i]);
observer.complete(events[i]);
});
And while at least that compiles, I'm not sure how to 'return' it at the right time... as I can't "Create" it outside the allEventsObserve.subscribe method (because 'events' doesn't exist) and can't (seem) to "return" it from within the subscribe. I'm also not entirely sure how I'd then "trigger" the 'next'...?
Do I need to modify the data within allEventsObserve and somehow simply still return that? Do I make a new observable (as attempted above) with the right payload - and if so, how do I trigger it? etc... I've checked here: How to declare an observable on angular2 but can't seem to follow how the 'second' observable gets triggered. Perhaps I have the entire paradigm wrong?
It appears that you're misunderstanding what an RxJS operator (like map, filter, etc) actually returns, and I think correcting that will make the solution clear.
Consider this short example:
allEventsObserve
.map(events => {
return 'this was an event';
})
Granted, it's a pretty useless example since all of the data from events is lost, but let's ignore that for now. The result of the code above is not an array of strings or anything else, it's actually another Observable. This Observable will just emit the string 'this was an event' for each array of events emitted by allEventsObserve This is what allows us to chain operators on observables -- each operator in the chain returns a new Observable that emits items that have been modified in some way be the previous operator.
allEventsObserve
.map(events => {
return 'this was an event';
})
.filter(events => typeof events !== 'undefined')
allEventsObserve is obviously an Observable, allEventsObserve.map() evaluates to an Observable, and so does allEventsObserve.map().filter().
So, since you're expecting your function to return an Observable, you don't want to call subscribe just yet, as doing so would return something that isn't really an Observable.
With that in mind, your code can be rewritten in the following way:
public getEvents(key:string,value:string) : Observable<Array<any>> {
var allEventsObserve : Observable<Array<any>> = this.getHTTPEvents();
return allEventsObserve
.map(events => {
var match = events.filter(event => event[key] == value);
if (match.length == 0) {
throw 'no matching event found';
} else {
return match[0];
}
})
.catch(e => {
console.log(e);
return e;
});
}
Since getEvents returns an Observable, somewhere else in your code you would do something like getEvents().subscribe(events => processEvents()) to interact with them. This code also assumes that this.getHTTPEvents() returns an Observable.
Also, notice that I changed your for loop to a call to filter, which operates on arrays. events in this case is a plain-old JavaScript Array, so the filter that is getting called is not the same filter as the RxJS operator filter.

How to do the chain sequence in rxjs

I would like to to something like:
this._myService.doSomething().subscribe(result => {
doSomething()
});
.then( () => dosthelse() )
.then( () => dosanotherthing() )
So I would like to chain .then like in promise. How would I do that in Rxjs?
this._myService.getLoginScreen().subscribe( result => {
window.location.href = MyService.LOGIN_URL;
/// I would like to wait for the site to load and alert something from the url, when I do it here it alerts the old one
});
.then (alert(anotherService.partOfTheUrl())
getLoginScreen() {
return this.http.get(myService.LOGIN_URL)
.flatMap(result => this.changeBrowserUrl())
.subscribe( result => //i want to do sth when the page is loaded//);
}
changeBrowserUrl(): Observable<any> {
return Observable.create( observer => {
window.location.href = myService.LOGIN_URL;
observer.next();
});
}
The equivalent of then for observables would be flatMap. You can see some examples of use here :
RxJS Promise Composition (passing data)
Why we need to use flatMap?
RxJS sequence equvalent to promise.then()?
For your example, you could do something like :
this._myService.doSomething()
.flatMap(function(x){return functionReturningObservableOrPromise(x)})
.flatMap(...ad infinitum)
.subscribe(...final processing)
Pay attention to the types of what your functions return, as to chain observables with flatMap you will need to return a promise or an observable.
If dosthelse or dosanotherthing returns a raw value, the operator to use is map. If it's an observable, the operator is flatMap (or equivalent).
If you want to do something imperatively. I mean outside the asynchronous processing chain, you could leverage the do operator.
Assuming that dosthelse returns an observable and dosanotherthing a raw object, your code would be:
this._myService.doSomething()
.do(result => {
doSomething();
})
.flatMap( () => dosthelse() )
.map( () => dosanotherthing() );
Notice that if you return the return of the subcribe method, it will correspond to a subscription object and not an observable. A subscription object is mainly for being able to cancel the observable and can't take part of the asynchronous processing chain.
In fact, most of the time, you subscribe at the end of the chain.
So I would refactor your code this way:
this._myService.getLoginScreen().subscribe( result => {
window.location.href = MyService.LOGIN_URL;
/// I would like to wait for the site to load and alert something from the url, when I do it here it alerts the old one
alert(anotherService.partOfTheUrl()
});
getLoginScreen() {
return this.http.get(myService.LOGIN_URL)
.flatMap(result => this.changeBrowserUrl())
.do( result => //i want to do sth when the page is loaded//);
}
changeBrowserUrl(): Observable<any> {
return Observable.create( observer => {
window.location.href = myService.LOGIN_URL;
observer.next();
});
}
Updated rxjs solution
Rxjs has changed quite a bit since this was answered.
flatMap is now mergeMap
Or switchMap, they're mostly interchangeable but it's good to know the difference
.do() is now tap()
Chaining is now done inside of a .pipe(). All manipulation should be done inside this pipe
You can chain pipes if needed (Ex. one variable maps an array of Users. Another variable takes that first variable and maps it a second time)
Do something after the original call has been made
Scenario
Make an HTTP call (Ex. Authentication check)
When that call has finished, navigate to another page
this._myService.getAuthenticated()
.pipe(
tap(result => this._myService.navigateToHome())
)
.subscribe()
Chain multiple calls
Scenario
Make an HTTP call (Ex. Authentication check)
Make a 2nd call to pull more info
Navigate after both calls have finished
this._myService.getAuthenticated()
.pipe(
// The Authentication call returns an object with the User Id
switchMap(user => this._myService.getUserInfo(user.id))
// After the user has been loaded, navigate
tap(user => this._myService.navigateToHome())
)
.subscribe()
Note on the above examples: I am assuming these calls are HTTP which unsubscribe after being called once. If you use a live observable (ex. a stream of Users), make sure you either unsubscribe or use takeUntil/first operators.
Example for Clarification (April, 2022)
The top of this pipe can emit n values (this means the chain will be called everytime a new value enters into the top of the pipe). In this example, n equals 3. This is a key difference between observables and promises. Observables can emit multiple values over time, but a promise cannot.
The subsequent chained streams emit one value (hence mimicing promises).
// Emit three values into the top of this pipe.
const topOfPipe = of<string>('chaining', 'some', 'observables');
// If any of the chained observables emit more than 1 value
// then don't use this unless you understand what is going to happen.
const firstObservable = of(1);
const secondObservable = of(2);
const thirdObservable = of(3);
const fourthObservable = of(4);
const addToPreviousStream = (previous) => map(current => previous + current);
const first = (one) => firstObservable.pipe(addToPreviousStream(one));
const second = (two) => secondObservable.pipe(addToPreviousStream(two));
const third = (three) => thirdObservable.pipe(addToPreviousStream(three));
const fourth = (four) => fourthObservable.pipe(addToPreviousStream(four));
// Pipeline of mergeMap operators, used for chaining steams together.
topOfPipe.pipe(
mergeMap(first),
mergeMap(second),
mergeMap(third),
mergeMap(fourth),
).subscribe(console.log);
// Output: chaining1234 some1234 observables1234
You could also use concatMap or switchMap. They all have subtle differences. See rxjs docs to understand.
mergeMap:
https://www.learnrxjs.io/learn-rxjs/operators/transformation/mergemap
concatMap:
https://www.learnrxjs.io/learn-rxjs/operators/transformation/concatmap
switchMap:
https://www.learnrxjs.io/learn-rxjs/operators/transformation/switchmap

Categories