in service section of angular.io tutorial for angular2 I hit a method named of.for example :
getHeroes(): Observable<Hero[]> {
return of(HEROES);
}
or in below sample:
getHero(id: number): Observable<Hero> {
// Todo: send the message _after_ fetching the hero
this.messageService.add(`HeroService: fetched hero id=${id}`);
return of(HEROES.find(hero => hero.id === id));
}
in angular.io Just explained
used RxJS of() to return an Observable of mock heroes
(Observable<Hero[]>).
and It was not explained why we should use of operator and exactly what does it do and what are its benefits?
The reason why they're using of() is because it's very easy to use it instead of a real HTTP call.
In a real application you would implement getHeroes() like this for example:
getHeroes(): Observable<Hero[]> {
return this.http.get(`/heroes`);
}
But since you just want to use a mocked response without creating any real backend you can use of() to return a fake response:
const HEROES = [{...}, {...}];
getHeroes(): Observable<Hero[]> {
return of(HEROES);
}
The rest of your application is going to work the same because of() is an Observable and you can later subscribe or chain operators to it just like you were using this.http.get(...).
The only thing that of() does is that it emits its parameters as single emissions immediately on subscription and then sends the complete notification.
Observable.of() is useful for maintaining the Observable data type before implementing an asynchronous interaction (for example, an http request to an API).
As Brandon Miller suggests, Observable.of() returns an Observable which immediately emits whatever values are supplied to of() as parameters, then completes.
This is better than returning static values, as it allows you to write subscribers that can handle the Observable type (which works both synchronously and asynchronously), even before implementing your async process.
//this function works synchronously AND asynchronously
getHeroes(): Observable<Hero[]> {
return Observable.of(HEROES)
//-OR-
return this.http.get('my.api.com/heroes')
.map(res => res.json());
}
//it can be subscribed to in both cases
getHeroes().subscribe(heroes => {
console.log(heroes); // '[hero1,hero2,hero3...]'
}
//DON'T DO THIS
getHeroesBad(): Array<Hero> {
return HEROES //Works synchronously
//-OR-
return this.http.get('my.api.com/heroes') //TypeError, requires refactor
}
In the first example, I assume that the HEROES variable is an array of objects, in which every object corresponds to interface Hero (or is the instance of class Hero). When we call this function somewhere in the code, it will act like this:
heroService.getHeroes()
.subscribe((heroes: Hero[]) => {
console.log(heroes)
// will output array of heroes: [{...}, {...}, ...]
})
It means, that when we use Observable's of creation method with array as argument, on subscription it will emit the whole array, not its' elements one-by-one
In the second example, when we subscribe to Observable, that was returned from getHero() method, it emits only one hero, whose id corresponds to given id.
Basically, when you create Observable with of method and supply arguments to it, on subscription it emits these arguments one-by-one
Here's a good reference
Related
I am trying to understand the observables RxJs and I am using angular framework! I can't understand what is actually happening in 'subscriber function' ,it has a parameter named 'observer', and this parameter has a method in the function body, and its name is next()!can a function parameter own a method? based on which rule?!
and the next question is : what is happening in the 'Observable' class? I think the subscriber function returns or to be more precise, creates and passes a value to the Observable instance! and when we call the subscribe method on 'customIntervalObservable' , it passes that data or value to the subscribe method?
am I right?
const customIntervalObservable = new Observable(function subscriber(
observer
) {
let count = 0;
setInterval(() => {
count++;
observer.next(+count);
if (count > 3) {
observer.error(new Error("count is greater than 3"));
}
}, 1000);
});
this.firstObjSubs = customIntervalObservable.subscribe(
(data: number) => {
console.log(data);
},
(error) => {
console.log(error), alert(error.message);
}
);
}
ngOnDestroy() {
//this.firstObjSubs.unsubscribe();
this.firstObjSubs.unsubscribe();
}
}
Here's a bit more:
You can think of an Observer as something that watches the Observable and reacts to notifications.
What notifications?
Next: When another item is emitted into the Observable
Error: When an error occurs
Complete: When there are no more items to emit
Observer is an object with three functions: one for each type of notification. You can define an Observer in code as shown below. But this is uncommon.
And then pass that Observer object into the subscribe as shown below:
More often, you'll pass either the next callback as shown in the first example below. Or an object with one, two, or three of the callback functions defined as shown in the second example.
The subscribe method tells the Observable stream to start emitting its values. It does not itself emit any values. Think of it as a streaming service, like Disney+ or hulu. You have to first subscribe to the service before you can stream movies.
Does this help?
can a function parameter own a method?
Javascript functions parameters are not typed, so you can pass any value you want - including an object.
what is happening in the 'Observable' class?
I'll give this a try:
An observable is basically just a wrapper around a - subscribe() - function that essentially describes the logic of the observable behaviour.
An observable has the ability to notify subjects - or subscribers, or observers - of changes occuring in its state during its lifecycle. To do so, a contract exists that states that the observable should call a subject next() method. This method should describe the logic for how the subject wants to react to such changes.
When a subject is interested in being notified by an observable of its state changes, it executes the observable subscribe() method, passing itself to it as an argument. This effectively provides the observable the ability to call the subject next() method whenever its logic dictates to do so.
To basically illustrate this in code:
// Function describing the observable logic.
function subscribe(observer){
// Observable logic, including calling observer.next() as many times as the logic dictates to notify the observer of state changes.
}
// Observable wrapper.
let observable = new Observable(subscribe)
// Subject interested in being notified of the observable state changes.
let observer = {
next(value){
// Logic for how to react to notifications from the observable.
}
}
// Effectively execute the observable logic.
observable.subscribe(observer)
For simplicty's sake, I omitted a number of more minor concepts - such as the subject complete() or error() methods, as well as the unsubscribe() function returned by the observable subscribe() function.
To avoid memory leaks in my Angular app i'm using the following well-known pattern to unsubscribe from Observables:
unsubscribe = new Subject();
ngOnInit() {
this.myService.getStuff()
.pipe(takeUntil(this.unsubscribe))
.subscribe(result => {
// processing the result
});
}
ngOnDestroy() {
this.unsubscribe.next();
}
This seemingly works fine, but in some examples i've noticed that complete() is also called on the Subject in addition to next():
ngOnDestroy() {
this.unsubscribe.next();
this.unsubscribe.complete(); // like this
}
Is calling complete() necessary here? If so, why? What are the consequences of not calling complete() in this scenario?
Let's see why you need to unsubscribe first.
Very simplified: Observable instance is holding an array of all subscriptions, which means every callback you have in your subscribe will be held in this array. This is bad news for Component because while it is referred from those functions it cannot be garbage-collected. I talk about these functions:
ngOnInit() {
this.myService.getStuff()
.subscribe(
result => null, // this function will be stored in Observable
error => null, // and this
() => null, // and even this
);
}
and it is applicable to every subscribe call.
Now you add a pipe .pipe(takeUntil(this.unsubscribe)) (or you can e.g. use my small library that does similar but shorter). In fact, your Observable subscribes to the events of Subject. And, whenever it emits a value, the Observable returned by this.myService.getStuff() will complete itself. That means all three functions above will be removed from this Observable's subscriptions array and your component is not referred from there anymore.
Problem solved.
All above you need to understand all the whys you have.
Here we finally come to your question
ngOnDestroy() {
this.unsubscribe.next();
this.unsubscribe.complete();
}
where complete is unnecessary, but not harming as well. Because the only subscriber to this subject was your Observable from this.myService.getStuff() (or other Observables from the same component). That means this Subject will refer to nothing else (the only listener is removed and complete that is supposed to clear all subscriptions is already empty), and as long as only component has reference to the Subject as its property, they both will be collected by garbage collector.
This has been discussed previously eg. here Why a 'next' before 'complete' of Angular takeuntil ngUnsubscribe?
You basically don't have to call complete() because next() will dispose the chain and takeUntil will unsubscribe from this.unsubscribe for you. Only if you had some other logic tied to this.unsubscribe then it might be necessary to call complete().
Anyway, you don't break anything if you do call complete().
I am trying to populate an array in my component called conventions which is an array of convention.
Each organization has a list of contracts, and each contract has a convention id, with this id i got the convention.
I use getOrganizationForUser to get current organization and then get the list of contract.
Then i use the convention id from contract to call the second API to get the convention.
Currently, my code looks something like this:
public getOrganizationForUser(): Observable<Organization> {
return this.httpClient
.get<Organization>(`${c.serviceBaseUrl.sp}/organizationByUser`)
.pipe(catchError((err, source) => this.responseHandler.onCatch(err, source)));
}
public getById(id: number) {
return this.httpClient
.get<Convention>(`${c.serviceBaseUrl.sp}/conventions/` + id)
.pipe(catchError((err, source) => this.responseHandler.onCatch(err, source)));
}
ngOnInit() {
this.OrganizationService.getOrganizationForUser().subscribe((organization: Organization) => {
organization.contracts.forEach((contract) => {
this.conventionService.getById(contract.conventionId).subscribe((convention: Convention) => {
this.conventions.push(convention);
})
})
})
}
I understand that I can create an array of observables, and use Observable.forkJoin() to wait for all these async calls to finish but I want to be able to define the subscribe callback
function for each of the calls since I need a reference to the process. Any ideas on how I can go about approaching this issue?
i tried with this function but always is return understand
getTasksForEachProcess(): Observable<Array<any>> {
let tasksObservables = this.organizationService.getOrganizationForUser().pipe(map((organization: Organization) => {
organization.contractOrganizations.map(contract => {
return this.conventionService.getById(contract.conventionId).subscribe(convention =>
this.conventions.push(convention)
)
});
})
);
return forkJoin(tasksObservables);
};
ngOnInit() {
this.getTasksForEachProcess().subscribe(item => {
console.log(item);
}
}
First of all I am not sure of what your are really trying to achieve, since I do not understand what you mean by
I want to be able to define the subscribe callback function for each
of the calls since I need a reference to the process
Anyways, in a situation like the one you describe, I would do something like this
public getOrganizationForUser(): Observable<Organization> {
return this.httpClient
.get<Organization>(`${c.serviceBaseUrl.sp}/organizationByUser`)
.pipe(catchError((err, source) => this.responseHandler.onCatch(err, source)));
}
public getById(id: number) {
return this.httpClient
.get<Convention>(`${c.serviceBaseUrl.sp}/conventions/` + id)
.pipe(catchError((err, source) => this.responseHandler.onCatch(err, source)));
}
ngOnInit() {
const obsOfObservables = this.OrganizationService.getOrganizationForUser()
.pipe(
map(organization => organization.contracts),
map(contracts => contracts.map(contract => this.conventionService.getById(contract.conventionId)))
);
obsOfObservables
.pipe(
switchMap(conventionObservables => forkJoin(conventionObservables))
)
.subscribe(
conventions => { // do stuff with conventions }
)
}
The key points here are the following.
Via getOrganizationForUser() you get an Observable which emits the Organization. The first thing you do you transform the object emitted by the Observable into an Array of contracts with the first map operator.
The second map operator transforms the Array of contracts into an Array of Observables of conventions. To perform this transformation we use the map method of Array within the map operator of Observable. This may be a bit confusing, but it is worth understanding.
If we stop here, what we have is obsOfObservables, i.e. an Observable which emits an Array of Observables.
We then pass the Array of Observables emitted by obsOfObservables to the forkJoin function, which in itself returns an Observable. Since we actually interested in what is notified by the Observable returned by forkJoin, i.e. we are interested in the conventions, then we need to switch from the first Observable to the second one, and this is done via switchMap operator.
The net result is an Observable which returns an Array of conventions. Consider that the constant obsOfObservables has been added as an attempt to clarify the reasoning and it is totally unnecessary (as Barney Panofsky would say).
I have not simulated the whole thing, so I hope I have not inserted mistakes, but more or less this is the thought process I would use in this case.
Last note, be generally suspicious when you have subscribe within subscibe.
I agree with Picci's logic in thinking through his answer. Here is a slight variation to what he proposed, though like him I have not rigorously tested this and there may be some errors.
The logic to this is that what you ultimately want is an array of convention, and producing an array from observables is what 'zip' does. So here is the flow:
get an organization, then create a stream of observables out of the organization.contracts array using rxjs' from.
each item in that stream will be a contract which will then be transformed (using map) into a convention based on and API lookup using the contract.conventionId property.
this whole resulting stream of observables of conventions will finally be transformed back into an array by the wrapping zip, and delivered as an observable that can be subscribed to resulting in the wanted array of conventions.
Here is the code:
ngOnInit() {
zip( this.OrganizationService.getOrganizationForUser()
.pipe(
map((organization: Organization) =>
from(organization.contracts).pipe(
map(contract => this.conventionService.getById(contract.conventionId))
)
)
)
)
.subscribe((conventions: Convention[]) => this.conventions = conventions)
}
Following situation: I make two async http calls.
one for getting a list of names
another for making something for each name in the list/array
For the sake of simplicity: Lets imagine the first call returns me a list of names:
['marta', 'edgar', 'david'].
And the second http call post the names in the database.
My implementation works fine for this operational requirement. It looks as follows:
public deployAllPartners(): void {
this.isDeploying = true;
this.getAllPartner().subscribe(shortname => this.adminService.deploySinglePartnerForTesting(shortname).subscribe());
}
private getAllPartner(): Observable<string> {
return this.partnerService.getPartnersOverview()
.flatMap((partnerList) => partnerList.partner) <== returns an array
.map((partner) => partner.shortname);
}
Problem:
Now what I want is that the boolean isDeploying is turning to false when the last name was deployed. Is there any RxJS Operator which is triggered when the last shortname is deployed? Maybe something like finally() or something along those line?. For completness: The boolean is there for a loading gif in the HTML and the gif is only showing up in the UI when isDeploying=true and of course is hidden when the value is false.
If you want to "end" the chain after a source Observable completes you can use concat or since RxJS 6.2.0 also the new endWith operator as well.
Or if you don't want to append any values and just do some side-effect you can use the complete handler in your subscribe() call.
import { from, of } from 'rxjs';
import { concat } from 'rxjs/operators';
from(['a', 'b', 'c'])
.pipe(
concat(of('end')),
)
.subscribe({
next: console.log,
complete: () => console.log('completed'),
});
See live demo: https://stackblitz.com/edit/rxjs6-demo-tplu6y?file=index.ts
Use finalize for RXJS6:
this.getAllPartner().
.pipe(
finalize(() => this.isDeploying = false)
)
I have two observables which I want to combine and in subscribe use either both arguments or only one. I tried .ForkJoin, .merge, .concat but could not achieve the behaviour I'm looking for.
Example:
obs1: Observable<int>;
obs2: Observable<Boolean>;
save(): Observable<any> {
return obs1.concat(obs2);
}
Then when using this function:
service.save().subscribe((first, second) => {
console.log(first); // int e.g. 1000
console.log(second); // Boolean, e.g. true
});
or
service.save().subscribe((first) => {
console.log(first); // int e.g. 1000
});
Is there a possibility to get exactly that behaviour?
Hope someone can help!
EDIT:
In my specific use case obs1<int> and obs2<bool> are two different post requests: obs1<int> is the actual save function and obs2<bool> checks if an other service is running.
The value of obs1<int> is needed to reload the page once the request is completed and the value of obs2<bool> is needed to display a message if the service is running - independant of obs1<int>.
So if obs2<bool> emits before obs1<int>, that's not a problem, the message gets display before reload. But if obs1<int> emits before obs2<bool>, the page gets reloaded and the message may not be displayed anymore.
I'm telling this because with the given answers there are different behaviours whether the values get emitted before or after onComplete of the other observable and this can impact the use case.
There are several operators that accomplish this:
CombineLatest
This operator will combine the latest values emitted by both observables, as shown in the marble diagram:
obs1: Observable<int>;
obs2: Observable<Boolean>;
save(): Observable<any> {
return combineLatest(obs1, obs2);
}
save().subscribe((val1, val2) => {
// logic
});
Zip
The Zip operator will wait for both observables to emit values before emitting one.
obs1: Observable<int>;
obs2: Observable<Boolean>;
save(): Observable<any> {
return zip(obs1, obs2);
}
save().subscribe((vals) => {
// Note Vals = [val1, val2]
// Logic
});
Or if you want to use destructuring with the array
save().subscribe(([val1, val2]) => {
// Logic
});
WithLatestFrom
The WithLatestFrom emits the combination of the last values emitted by the observables, note this operator skips any values that do not have a corresponding value from the other observable.
save: obs1.pipe(withLatestFrom(secondSource))
save().subscribe(([val1, val2]) => {
// Logic
});
You can use forkJoin for this purpose. Call them parallely and then if either of them is present then do something.
let numberSource = Rx.Observable.of(100);
let booleanSource = Rx.Observable.of(true);
Rx.Observable.forkJoin(
numberSource,
booleanSource
).subscribe( ([numberResp, booleanResp]) => {
if (numberResp) {
console.log(numberResp);
// do something
} else if (booleanResp) {
console.log(booleanResp);
// do something
}
});
You may use the zip static method instead of concat operator.
save(): Observable<any> {
return zip(obs1, obs2);
}
Then you should be able to do like the following:
service.save().subscribe((x) => {
console.log(x[0]); // int e.g. 1000
console.log(x[1]); // Boolean, e.g. true
});
The exact operator to use depends on the specific details of what you are trying to solve.
A valid option is to use combineLatest - Docs:
obs1$: Observable<int>;
obs2$: Observable<Boolean>;
combined$ = combineLatest(obs1$, obs2$);
combined$.subscribe(([obs1, obs2]) => {
console.log(obs1);
console.log(obs2);
})
Concat emits two events through the stream, one after the other has completed, this is not what you're after.
Merge will emit both events in the same manner, but in the order that they actually end up completing, also not what you're after.
What you want is the value of both items in the same stream event. forkJoin and zip and combineLatest will do this, where you're getting tripped up is that they all emit an array of the values that you're not accessing properly in subscribe.
zip emits every time all items zipped together emit, in sequence, so if observable 1 emits 1,2,3, and observable two emits 4,5; the emissions from zip will be [1,4], [2,5].
combineLatest will emit everytime either emits so you'll get soemthing like [1,4],[2,4],[2,5],[3,5] (depending on the exact emission order).
finally forkJoin only emits one time, once every item inside it has actually completed,a and then completes itself. This is likely what you want more than anything since you seem to be "saving". if either of those example streams don't complete, forkJoin will never emit, but if they both complete after their final value, forkjoin will only give one emission: [2,5]. I prefer this as it is the "safest" operation in that it guarantees all streams are completing properly and not creating memory leaks. And usually when "saving", you only expect one emission, so it is more explicit as well. When ever you see forkJoin, you know you're dealing with a single emission stream.
I would do it like this, personally:
obs1: Observable<int>;
obs2: Observable<Boolean>;
save(): Observable<any> {
return forkJoin(obs1, obs2);
}
service.save().subscribe(([first, second]) => {
console.log(first); // int e.g. 1000
console.log(second); // Boolean, e.g. true
});
Typescript provides syntax like this to access the items in an array of a known length, but there is no way to truly create multiple arguments in a subscribe success function, as it's interface only accepts a single argument.