问题
I have a generic service for get data from server. When response received, I'm using a mapper function to map pure JSON data to required model. For some class type in mapper function, I need to get some additional data from server. How can I forcing mapper function wait for second request?
This is my get function:
getChildren(params: ITreeQueryParams): Observable<Optional<T[]>> {
params.id = isPresent(params.id) ? params.id : 0;
params.parentId = isPresent(params.parentId) ? params.parentId : 0;
params.isRoot = isPresent(params.isRoot) ? params.isRoot : false;
params.additionalId = isPresent(params.additionalId) ?
params.additionalId : 0;
return this.http.get<IListResponse<T>>
(`${this.resourceUrl}/getChildren/${params.id}/${params.parentId}/${params.isRoot}/${params.additionalId}`,
{
observe: 'response'
}).pipe(map((resp) => this.mapResponse(resp,this.model)));
}
This is my mapper function:
protected mapResponse(resp: any, model: IAsset): void {
if (resp) {
this.anotherTreeService.getNodeDetail(resp.id, resp.isRoot).subscribe(res => {
model.additionalData = {canEdit: res.length > 0 ? true : false};
});
if (resp.name) {
model.title = resp.name;
}
}
}
回答1:
There's plenty of operators in RxJS that achieve what you want. What you see used quite often, when a data model in itself does not contain all the data and you need a separate call to retrieve additional data based on the first result is an example like this:
this.myFirstService.getById(id).pipe(
map(data => jsonToMyModel(data)),
tap(model => this.data = model),
switchMap(model => {
return this.mySecondService.getListById(model.id)
}),
tap(secondData => this.data.list = secondData)
).subscribe()
This changes on what you're exactly trying to achieve, as it is possible to have all the data end up in one model returned by the Observable, by using forkJoin()
or mergeMap()
. Any of these operators have their usage in these type of situations.
Hope this helps. A more clear and direct example of what you want would require a more complete question with some examples of what you want and what you've tried.
回答2:
If I understand you right you simply want to make one request and make for it sub requests.
For that purpose you can use switchMap
and forkJoin
if you need to take many sub requests.
Here is how you can rewrite your code (I'll write a simplified version)
getChildren() {
this.http.someRequest.pipe(
tap(response => {
if (resp.name) {
model.title = resp.name;
}
}),
switchMap(response =>
forkJoin(
response.map(r => this.http.someRequest(r))
)
)
)
}
What switchMap
does is after each source emission (your first observable) it subscribes to the returning observable in a callback (if it was subscribed already it unsubscribes first from callback observable).
forkJoin
works like Promise.all
. You can pass there an Array, Object or Multiple observables (now deprecated) and in subscribe you get either an Array or an Object.
Please note that forkJoin
will emit last value emmitted by each observable only when all observables complete. Also, if any observable completes without emitting value forkJoin
completes without emitting value either. Since all http.whatever
are observables with one next
and immediate complete
after that you don't have to be worried about that.
Also if any observable errors forkJoin
errors.
来源:https://stackoverflow.com/questions/57283581/how-to-handle-nested-http-requests-in-angular-7