This is somewhat related to a previous question I asked. The feature$ function in that question returns an observable with a map that uses the parameter passed to the function:
feature$ = (feature: string): Observable<FeatureConfig | null> => {
return this.features$.pipe(
map((features: FeatureConfig[]) => {
return (
features.find((featureConfig: FeatureConfig) => {
return featureConfig.key === feature;
})?.value ?? null
);
})
);
};
This is then used like this elsewhere:
this.featureService
.feature$("featureName")
.subscribe((featureConfig: FeatureConfig) => {
...
});
Or:
someFeature$ = this.featureService.feature$("featureName");
The features$ observable is (I think, by definition) a hot observable as its value can change throughout the life of the observable and it never completes. While this seems to work for its intended purpose, I am just wondering what the effect this has when there are many subscribers to that feature$ function. I fear there might be some unintended behavior that I am not immediately noticing.
Is this a bad pattern in general? And if so, is there a better pattern to do something similar? That is, subscribe to an observable created with a parameter passed to a function.
For example, would something like this be preferred?
feature$ = (featureName: string): Observable<FeatureConfig | null> => {
return of(featureName).pipe(
mergeMap((feature: string) => combineLatest([of(feature), this.features$])),
map(([feature, features]: [string, FeatureConfig[]]) => {
return (
features.find((featureConfig: FeatureConfig) => {
return featureConfig.key === feature;
})?.value ?? null
);
})
);
};
Or does it matter?
The the second stream example is a bit overly complicated, your features$$ is a Behavior subject that might continuously updating itself. Your intend is only take in parameter and process through the features array and output the found feature, the first form of the code is more appropriate.
As the source stream is a BehaviorSubject you will always have a value once subscribe(), just don't forget to unsubcribe() to prevent memory leak. Alternatively use take(1) or first() operator before subscribe()
When you create an observable from a function you get a new instance of that stream, it is a hot observable but not shared(), so filtering on 'featureA' wouldn't affect result on filtering on 'featureB', and yes of() and combineLatest() really does nothing in your use case, as those are static and unchange function param
In the method below I want to call two observables. After the data from first observable (getUnrecoveredGearsExt- a http req) is returned I want to pass the data to the second observable (createUpdate- persist to indexDB). Is there a cleaner way to achieve this maybe using some of the rxjs operators. thanks
Note: after the successful completion of the second observable I want to return the data from the first Observable. The use case is get data from the backend and store locally in indexDB and if successful return data or error
public getAndUpdateUnrecoveredGears(cfr: string, maxResults?: number, excludeTripid?: string) : Observable<GearSet[]> {
return Observable.create((observer) => {
this.getUnrecoveredGearsExt(cfr,maxResults,excludeTripid).subscribe(
(gears: GearSet[]) => {
this.createUpdate(gears).subscribe(
() => {
observer.next(gears);
observer.complete();
},
(error) => {
observer.error(error);
}
);
},
(error) => {
observer.error(error);
}
);
});
}
Having nested .subscribe() methods is an anti-pattern of RxJS and can cause many issues. So it's a strong signal of when you need to use operators. Fortunately, there is one which simplifies your code.
public getAndUpdateUnrecoveredGears(cfr: string, maxResults?: number, excludeTripid?: string) : Observable<GearSet[]> {
return this.getUnrecoveredGearsExt(cfr,maxResults,excludeTripid).pipe(
concatMap((gears:GearSet[])=>this.createUpdate(gears))
);
}
Because we're dealing with HTTP requests, they'll emit one value then complete. For this, we can use concatMap(). This operator will wait until getUnrecoveredGearsExt() completes, and then will subscribe to createUpdate() using the value emitted from getUnrecoveredGearsExt(). The operator will then emit any values coming from this "inner observable".
Assuming createUpdate() is also an HTTP request, it will automatically send a complete signal after emitting the response.
The solution below works for me. The final issue was how to pass the previous result 'gears' out as a final result. This is achieved by using the combineLatest to pass the two results to the next map operator, which can then pass gears out.
public getAndUpdateUnrecoveredGearsAlt2(cfr: string, maxResults?: number, excludeTripid?: string): Observable<GearSet[]> {
return this.getUnrecoveredGearsExt(cfr,maxResults,excludeTripid).pipe(
switchMap((gears: GearSet[]) => { return combineLatest(this.createUpdate(gears),of(gears));}),
map(([temp, gears]) => gears )
);
}
I have an Angular 2 service:
import {Storage} from './storage';
import {Injectable} from 'angular2/core';
import {Subject} from 'rxjs/Subject';
#Injectable()
export class SessionStorage extends Storage {
private _isLoggedInSource = new Subject<boolean>();
isLoggedIn = this._isLoggedInSource.asObservable();
constructor() {
super('session');
}
setIsLoggedIn(value: boolean) {
this.setItem('_isLoggedIn', value, () => {
this._isLoggedInSource.next(value);
});
}
}
Everything works great. But I have another component which doesn't need to subscribe, it just needs to get the current value of isLoggedIn at a certain point in time. How can I do this?
A Subject or Observable doesn't have a current value. When a value is emitted, it is passed to subscribers and the Observable is done with it.
If you want to have a current value, use BehaviorSubject which is designed for exactly that purpose. BehaviorSubject keeps the last emitted value and emits it immediately to new subscribers.
It also has a method getValue() to get the current value.
The only way you should be getting values "out of" an Observable/Subject is with subscribe!
If you're using getValue() you're doing something imperative in declarative paradigm. It's there as an escape hatch, but 99.9% of the time you should NOT use getValue(). There are a few interesting things that getValue() will do: It will throw an error if the subject has been unsubscribed, it will prevent you from getting a value if the subject is dead because it's errored, etc. But, again, it's there as an escape hatch for rare circumstances.
There are several ways of getting the latest value from a Subject or Observable in a "Rx-y" way:
Using BehaviorSubject: But actually subscribing to it. When you first subscribe to BehaviorSubject it will synchronously send the previous value it received or was initialized with.
Using a ReplaySubject(N): This will cache N values and replay them to new subscribers.
A.withLatestFrom(B): Use this operator to get the most recent value from observable B when observable A emits. Will give you both values in an array [a, b].
A.combineLatest(B): Use this operator to get the most recent values from A and B every time either A or B emits. Will give you both values in an array.
shareReplay(): Makes an Observable multicast through a ReplaySubject, but allows you to retry the observable on error. (Basically it gives you that promise-y caching behavior).
publishReplay(), publishBehavior(initialValue), multicast(subject: BehaviorSubject | ReplaySubject), etc: Other operators that leverage BehaviorSubject and ReplaySubject. Different flavors of the same thing, they basically multicast the source observable by funneling all notifications through a subject. You need to call connect() to subscribe to the source with the subject.
I had similar situation where late subscribers subscribe to the Subject after its value arrived.
I found ReplaySubject which is similar to BehaviorSubject works like a charm in this case.
And here is a link to better explanation: http://reactivex.io/rxjs/manual/overview.html#replaysubject
const observable = of('response')
function hasValue(value: any) {
return value !== null && value !== undefined;
}
function getValue<T>(observable: Observable<T>): Promise<T> {
return observable
.pipe(
filter(hasValue),
first()
)
.toPromise();
}
const result = await getValue(observable)
// Do the logic with the result
// .................
// .................
// .................
You can check the full article on how to implement it from here.
https://www.imkrish.com/blog/development/simple-way-get-value-from-observable
I encountered the same problem in child components where initially it would have to have the current value of the Subject, then subscribe to the Subject to listen to changes. I just maintain the current value in the Service so it is available for components to access, e.g. :
import {Storage} from './storage';
import {Injectable} from 'angular2/core';
import {Subject} from 'rxjs/Subject';
#Injectable()
export class SessionStorage extends Storage {
isLoggedIn: boolean;
private _isLoggedInSource = new Subject<boolean>();
isLoggedIn = this._isLoggedInSource.asObservable();
constructor() {
super('session');
this.currIsLoggedIn = false;
}
setIsLoggedIn(value: boolean) {
this.setItem('_isLoggedIn', value, () => {
this._isLoggedInSource.next(value);
});
this.isLoggedIn = value;
}
}
A component that needs the current value could just then access it from the service, i.e,:
sessionStorage.isLoggedIn
Not sure if this is the right practice :)
A similar looking answer was downvoted. But I think I can justify what I'm suggesting here for limited cases.
While it's true that an observable doesn't have a current value, very often it will have an immediately available value. For example with redux / flux / akita stores you may request data from a central store, based on a number of observables and that value will generally be immediately available.
If this is the case then when you subscribe, the value will come back immediately.
So let's say you had a call to a service, and on completion you want to get the latest value of something from your store, that potentially might not emit:
You might try to do this (and you should as much as possible keep things 'inside pipes'):
serviceCallResponse$.pipe(withLatestFrom(store$.select(x => x.customer)))
.subscribe(([ serviceCallResponse, customer] => {
// we have serviceCallResponse and customer
});
The problem with this is that it will block until the secondary observable emits a value, which potentially could be never.
I found myself recently needing to evaluate an observable only if a value was immediately available, and more importantly I needed to be able to detect if it wasn't. I ended up doing this:
serviceCallResponse$.pipe()
.subscribe(serviceCallResponse => {
// immediately try to subscribe to get the 'available' value
// note: immediately unsubscribe afterward to 'cancel' if needed
let customer = undefined;
// whatever the secondary observable is
const secondary$ = store$.select(x => x.customer);
// subscribe to it, and assign to closure scope
sub = secondary$.pipe(take(1)).subscribe(_customer => customer = _customer);
sub.unsubscribe();
// if there's a delay or customer isn't available the value won't have been set before we get here
if (customer === undefined)
{
// handle, or ignore as needed
return throwError('Customer was not immediately available');
}
});
Note that for all of the above I'm using subscribe to get the value (as #Ben discusses). Not using a .value property, even if I had a BehaviorSubject.
Although it may sound overkill, this is just another "possible" solution to keep Observable type and reduce boilerplate...
You could always create an extension getter to get the current value of an Observable.
To do this you would need to extend the Observable<T> interface in a global.d.ts typings declaration file. Then implement the extension getter in a observable.extension.ts file and finally include both typings and extension file to your application.
You can refer to this StackOverflow Answer to know how to include the extensions into your Angular application.
// global.d.ts
declare module 'rxjs' {
interface Observable<T> {
/**
* _Extension Method_ - Returns current value of an Observable.
* Value is retrieved using _first()_ operator to avoid the need to unsubscribe.
*/
value: Observable<T>;
}
}
// observable.extension.ts
Object.defineProperty(Observable.prototype, 'value', {
get <T>(this: Observable<T>): Observable<T> {
return this.pipe(
filter(value => value !== null && value !== undefined),
first());
},
});
// using the extension getter example
this.myObservable$.value
.subscribe(value => {
// whatever code you need...
});
There are two ways you can achieve this.
BehaviorSubject has a method getValue() which you can get the value in a specific point of time.
You can subscribe directly with the BehaviorSubject and you may pass the subscribed value to a class member, field or property.
I wouldn't recommend both approaches.
In the first approach, it's a convenient method you can get the value anytime, you may refer to this as the current snapshot at that point of time. Problem with this is you can introduce race conditions in your code, you may invoke this method in many different places and in different timing which is hard to debug.
The second approach is what most developers employ when they want a raw value upon subscription, you can track the subscription and when do you exactly unsubscribe to avoid further memory leak, you may use this if you're really desperate to bind it to a variable and there's no other ways to interface it.
I would recommend, looking again at your use cases, where do you use it? For example you want to determine if the user is logged in or not when you call any API, you can combine it other observables:
const data$ = apiRequestCall$().pipe(
// Latest snapshot from BehaviorSubject.
withLatestFrom(isLoggedIn),
// Allow call only if logged in.
filter(([request, loggedIn]) => loggedIn)
// Do something else..
);
With this, you may use it directly to the UI by piping data$ | async in case of angular.
A subscription can be created, then after taking the first emitted item, destroyed. In the example below, pipe() is a function that uses an Observable as its input and returns another Observable as its output, while not modifying the first observable.
Sample created with Angular 8.1.0 packages "rxjs": "6.5.3", "rxjs-observable": "0.0.7"
ngOnInit() {
...
// If loading with previously saved value
if (this.controlValue) {
// Take says once you have 1, then close the subscription
this.selectList.pipe(take(1)).subscribe(x => {
let opt = x.find(y => y.value === this.controlValue);
this.updateValue(opt);
});
}
}
You could store the last emitted value separately from the Observable. Then read it when needed.
let lastValue: number;
const subscription = new Service().start();
subscription
.subscribe((data) => {
lastValue = data;
}
);
The best way to do this is using Behaviur Subject, here is an example:
var sub = new rxjs.BehaviorSubject([0, 1])
sub.next([2, 3])
setTimeout(() => {sub.next([4, 5])}, 1500)
sub.subscribe(a => console.log(a)) //2, 3 (current value) -> wait 2 sec -> 4, 5
Another approach, If you want / can to use async await (has to be inside of an async functions) you can do this with modern Rxjs:
async myFunction () {
const currentValue = await firstValueFrom(
of(0).pipe(
withLatestFrom(this.yourObservable$),
map((tuple) => tuple[1]),
take(1)
)
);
// do stuff with current value
}
This will emit a value "Right away" because of withLatestFrom, and then will resolve the promise.
The goal is to iterate through a collection of IDs, making an HTTP call for each ID. For each ID, I'm using a service with a get() method that returns an Observable. Each time the get() method is called, I'm subscribing to the returning Observable and trying to push the result into an array, which will eventually get passed on to a different method for a new operation.
Relevant service method:
public get(departmentId: number): Observable<IDepartmentModel> {
return super.get<IDepartmentModel>(departmentId);
}
note: the super class is leveraging Angular Http, which is well tested and confirmed to be working correctly. The problem with the logic isn't here...
Relevant component methods:
note the departmentService.get() call that's being called several times within the forEach.
setInitialDepartmentsAssignedGridData(): void {
this.settingsForDropdownSelectedCompanyId = this.userForm.get('defaultCompany').get('defaultCompanyId').value;
let departments: IDepartmentModel[] = [];
this.userService.user.getValue() //confirmed: valid user is being pulled back from the userService (logic is fine here..)
.userCompanies.find(comp => comp.companyId === this.settingsForDropdownSelectedCompanyId) // getting a valid match here (logic is fine here..)
.departmentIds.forEach(deptId => this.departmentService.get(deptId).first().subscribe(dept => { // getting a valid department back here (logic is fine here...)
departments.push(dept); // HERE LIES THE PROBLEM
}));
this.setDepartmentsAssignedRowData(departments);
}
setDepartmentsAssignedRowData(departments: IDepartmentModel[]): void {
console.log('setDeptAssignedRowData called'); // confirmed: method is getting called...
console.log(departments); // confirmed: fully-composed collection of departments logged to the console...
departments.forEach(dept => {
console.log(dept);
}); // Y U NO WORK!?
departments.map((department) => {
console.log(department); // Y U NO WORK?
this.departmentAssignedRowData.push({
departmentId: department.id,
departmentName: department.name
});
});
this.departmentAssignedGridOptions.api.setRowData(this.departmentAssignedRowData);
}
The problem is, although what's getting logged to the console is a fully-composed department-objects array, it's not TRULY "there"; what's getting passed to setDepartmentsAssignedRowData is an empty array.
I'm sure what's happening is that the async operations are not complete before the departments array gets passed to the second method. Some of what I've read online says to use forkJoin, but I can't see how that will look in this context. I've also read concatMap may work, but again, in this context, I'm not sure how to make that work...
In this context, how do I leverage RxJS to make sure the intended, fully-composed departments array is truly ready to be passed?
thanks for any insight you can provide. help is much appreciated!
You are correct, you need forkJoin
let observableArray = this.userService.user.getValue()
.userCompanies.find(comp => comp.companyId === this.settingsForDropdownSelectedCompanyId)
.departmentIds.map(deptId => this.departmentService.get(deptId)) // map is building out an array of observables
This will be an array of http request observables that you want to make in parallel. Now you can pass this array to forkJoin.
Observable.forkJoin(...observableArray)
The return of forkJoin will be an array of results from observableArray. forkJoin will not emit to the next operator in the sequence until all of the observables in observableArray have completed (so when all of the http requests have finished)
So altogether the code will be
let observableArray = this.userService.user.getValue()
.userCompanies.find(comp => comp.companyId === this.settingsForDropdownSelectedCompanyId)
.departmentIds.map(deptId => this.departmentService.get(deptId));
Observable.forkJoin(...observableArray).subscribe(res => {
// res = [resId0, resId1, resId2, ..., resIdX];
});
You mentioned passing the result to another operator. If that operator is another http request where you pass an array of data (from forkJoin), then you can use the flatMap operator.
Observable.forkJoin(...observableArray)
.flatMap(res => {
return this.otherApi(res);
})
.subscribe(res => {
// res is the result of the otherApi call
});
flatMap will chain your api requests together. So altogether what is happening is
run array of observables in parallel
once complete, run second api (otherApi)
I am playing around in Java 8. How can I return a method reference?
I am able to return a lambda but not the method reference.
My attempts:
public Supplier<?> forEachChild(){
return new ArrayList<?>::forEach;
}
OR
public Function<?> forEachChild(){
return new ArrayList<?>::forEach;
}
You have a small mis-understanding of how method-references work.
First of all, you cannot new a method-reference.
Then, let's reason through what you want to do. You want the method forEachChild to be able to return something that would accept a List and a Consumer. The List would be on which object to invoke forEach on, and the Consumer would be the action to perform on each element of the list. For that, you can use a BiConsumer: this represents an operation taking 2 parameters and returning no results: the first parameter is a list and the second parameter is a consumer.
As such, the following will work:
public <T> BiConsumer<List<T>, Consumer<? super T>> forEachChild() {
return List::forEach;
}
This type of method-reference is called "Reference to an instance method of an arbitrary object of a particular type". What happens is that the first parameter of type List<T> becomes the object on which forEach will be invoked, by giving it as parameter the Consumer.
Then you can use it like:
forEachChild().accept(Arrays.asList("1", "2"), System.out::println);
I would like to add some points.
You can't instantiate unbounded type instance.
List<?> list = new ArrayList<?>();
Second, as Tunaki mentioned, you can't make references to new MyObject::staticMethod when you make method references
The other thing is, forEach(Consumer<T> consumer) (Terminal operation for pipeline streams) doesn't return anything. It only eats whatever we feed it.
-Hope this may help :)