I'm implementing an angular service that lets consumers observe various values based on their id:
The essence of it looks like this:
private subjects = new Map<number, Subject<any>>();
public subscribe(id: number, observer: any): Subscription {
// try getting subject for this id (or undefined if it does not yet exist)
let subj = this.subjects.get(id);
// create subject if it does not yet exist
if (!subj) {
subj = new Subject<any>();
this.subjects.set(id, subj);
}
// subscribe observer
const subscription = subj.subscribe(observer);
// set up teardown logic (gets called when subscription is unsubscribed)
subscription.add(() => {
// remove subject from the map, if this was the last subscription
if (subj.observers.length == 0) {
this.subjects.delete(id);
}
});
// return subscription
return subscription;
}
Here is the full stackblitz example
The above works fine but the API is a bit cumbersome to use (in the consumers I need to manually keep track of all the subscriptions and make sure to unsubscribe them properly).
I would prefer to have a method that returns an Observable
like this:
public subscribe(id: number): Observable<any> {
// TODO: Return an observable for this id and make sure that
// its corresponding subject is in the map iff at least one of the observables
// for this id has at least one subscription.
return ...;
}
Because this would allow me to subscribe to the values I need directly from the component templates using the async
pipe, where angular would take care of unsubscribing the observers.
But I can't quite figure out how I can implement the logic to remove unused Subject
s from the Map
when they are no longer used. Is there a good way to do that?
Here is an incomplete stackblitz examples with some test cases