问题
I'm working on a user service that keeps track of users by their user_id. It first checks if user_id is present in their cookies, if it isn't it makes a get request to the api to create a new one and returns the id in the response. My problem is that the user_id is being consumed before the response is completed. I have two questions:
- The BehaviourSubject is being consumed before it's even defined as a new BehaviourService, components that consume it are calling .subscribe() on
undefined
as a result and the app is crashing. - Is the really necessary for me to subscribe to the user_id every single time I want to retrieve it? The code is turning into hell to work with since i'm subscribing to the behavioursubject first and then inside the subscribe method I'm writing my code. I can't figure out a better way to go about it, and from the texts I've read they mention that linking streams is the right approach, but this just feels very wrong.
Here's a simplified example of what I'm doing
get ruid or create one - user.service.ts
constructor(...) {
public ruid: BehaviorSubject<any>;
if(!Cookies.get('ruid')) {
this.http.get(url).subscribe(
(value) => {
this.ruid = new BehaviorSubject(value)
Cookies.set('ruid', value)
}
)
} else {
this.ruid = new BehaviorSubject(Cookie.get('ruid'));
}
}
use ruid in component
constructor(private userService: UserService) {
data;
this.userService.ruid.subscribe(
(value) => {
this.data = this.http.get(url + value).map(res => res.json())
}
);
}
回答1:
I believe what you need here is to setup resolver on your route. See my example from real project below:
resolver.ts
@Injectable()
export class LocationResolver implements Resolve<any> {
constructor(private locationsApiService: LocationsApiService, private appStorage: AppStorage, private authService: AuthService) {
}
resolve(route: ActivatedRouteSnapshot): Observable<any> {
return new Observable((observer) => {
const sessionData = this.authService.getSessionData();
if (!sessionData.Location) {
this.locationsApiService.getUserLocations().subscribe(
locations => {
sessionData.Location = locations[0].Name;
sessionData.LocationId = locations[0].Id;
this.authService.updateSessionData(sessionData);
this.appStorage.set(AppConstants.storage.userLocations, locations);
observer.next(sessionData.LocationId);
observer.complete();
}
);
} else {
observer.next(sessionData.LocationId);
observer.complete();
}
});
}
}
Routes
{
path: 'app',
component: MainComponent,
canActivate: [AuthGuard],
resolve: {
locationId: LocationResolver
},
children: [...]
}
Basically what it does is not resolving route up until it gets the necessary data. In my case I need to query locationId and provide it to every call after login. So resolver either looks for it in LocalStorage or doing a call to the API and then sets the location.
UPD: In your case you can put such resolver on the route that subscribes to BehaviorSubject and it will not get instantiated and subscribed until you finish all the API calls.
来源:https://stackoverflow.com/questions/47170450/how-do-you-prevent-a-behavioursubject-from-being-consumed-before-its-defined