英文:
forkjoin using javascript object or sourcesObject implementation
问题
I have a piece of code that looks like this
getInformations().subscribe(
informations => {
let subs = [];
for (const information of informations) {
subs.push(getOtherDetails(information.id));
}
forkJoin(subs).subscribe(response => {
//How can I Associate Information Id With The Response
howToAssociateIdWithResponse();
}}
);
Situation - I want to tie the responses of my second call with the ids of my first call, but I am running into issues.
Attempted - I tried the following but that seems to be throwing an error
let subs: {[x:number]: Observable<any>[]} = [];
subs.push({information.id: getOtherDetails(info.id)})
but when I subscribed I got an error stating You provided an invalid object where a stream was expected. You can provide an Observable, Promise, Array, or Iterable.
Update - 1 After following @BizzyBob suggestion, the code looks like the following but my other logic is running before the subscription completes its job. Here is what I mean
async ngOnChanges(){
await getData(); //How to make sure below method only executes when this is really done.
await useInformationReceivedFromgetData();
}
async getData(){
getInformations().subscribe(
informations => {
let subs = [];
for (const information of informations) {
subs.push(getOtherDetails(information.id).pipe(
map(data => ({ id: information.id, data })) // <---
));
}
forkJoin(subs).subscribe(objects => {
objects.forEach(({id, data}) => { /* saved to an array */ });
});
}
);
}
(Note: The code includes HTML entities for angle brackets, which are commonly used in HTML to display code. If you want to use this code in an actual programming context, you may need to remove or replace these HTML entities with the appropriate symbols.)
英文:
I have a piece of code that looks like this
getInformations().subscribe(
informations => {
let subs = [];
for (const information of informations) {
subs.push(getOtherDetails(information.id));
}
forkJoin(subs).subscribe(response => {
//How can I Associate Information Id With The Response
howToAssociateIdWithResponse();
}}
);
Situation - I want to tie the responses of my second call with the ids of my first call, but I am running into issues.
Attempted - I tried the following but that seems to be throwing error
let subs: {[x:number]: Observable<any>}[] = [];
subs.push({information.id: getOtherDetails(info.id)})
but when I subscribed I got an error stating You provided an invalid object where a stream was expected. You can provide an Observable, Promise, Array, or Iterable.
Update - 1 After following @BizzyBob suggestion, the code looks like the following but my other logic is running before the subscription completes it's job. Here is what I mean
async ngOnChanges(){
await getData(); //How to make sure below method only executes when this is really done.
await useInformationReceivedFromgetData();
}
async getData(){
getInformations().subscribe(
informations => {
let subs = [];
for (const information of informations) {
subs.push(getOtherDetails(information.id).pipe(
map(data => ({ id: information.id, data })) // <---
));
}
forkJoin(subs).subscribe(objects => {
objects.forEach(({id, data}) => { /* saved to an arrray */ });
});
}
);
}
答案1
得分: 1
以下是翻译好的部分:
你可以使每个 "getOtherDetails observable" 发出一个带有 `id` 和响应 `data` 的对象:
```typescript
getInformations().subscribe(
informations => {
let subs = [];
for (const information of informations) {
subs.push(getOtherDetails(information.id).pipe(
map(data => ({ id: information.id, data })) // <---
));
}
forkJoin(subs).subscribe(objects => {
objects.forEach(({id, data}) => { /* 在这里使用 id 和 data */ });
});
}
);
请注意,您可以通过使用 .map() 来简化您的代码,而不是创建一个 subs
数组并将其添加到其中:
getInformations().subscribe(
informations => {
const subs = informations.map(
({id}) => getOtherDetails(id).pipe(map(data => ({ id, data })))
);
forkJoin(subs).subscribe(responses => {
responses.forEach(({id, data}) => { /* 在这里使用 id 和 data */ });
});
}
);
此外,将订阅放在订阅内部是不好的做法。您最好使用高阶映射运算符来处理 "inner subscription"。在这种情况下,我们可以使用 switchMap
来处理对您的 forkJoin observable 的订阅/取消订阅:
getInformations().pipe(
map(informations => informations.map(
({id}) => getOtherDetails(id).pipe(map(response => ({ id, data })))
)),
switchMap(requests => forkJoin(requests))
).subscribe(
responses => responses.forEach(({id, data}) => { ... })
);
英文:
You can make each "getOtherDetails observable" emit an object with the id
and response data
:
getInformations().subscribe(
informations => {
let subs = [];
for (const information of informations) {
subs.push(getOtherDetails(information.id).pipe(
map(data => ({ id: information.id, data })) // <---
));
}
forkJoin(subs).subscribe(objects => {
objects.forEach(({id, data}) => { /* use id and data here */ });
});
}
);
Note, you can simplify your code by using .map() instead of creating a subs
array and pusing to it:
getInformations().subscribe(
informations => {
const subs = informations.map(
({id}) => getOtherDetails(id).pipe(map(data => ({ id, data })))
);
forkJoin(subs).subscribe(responses => {
responses.forEach(({id, data}) => { /* use id and data here */ });
});
}
);
Also, putting subscribes inside of subscribes is bad news. You'd be better off to use a Higher Order Mapping Operator that will handle an "inner subscription" for you. In this case, we can use switchMap
to handle subscribing / unsubscribing to your forkJoin observable:
getInformations().pipe(
map(informations => informations.map(
({id}) => getOtherDetails(id).pipe(map(response => ({ id, data })))
),
switchMap(requests => forkJoin(requests))
).subscribe(
responses => responses.forEach(({id, data}) => { ... })
);
答案2
得分: 1
你可以更加依赖rxjs
及其管道操作符。考虑以下代码:
const combinedResults = getInformations().pipe(
mergeMap((informations) => {
return forkJoin(
informations.map((information) =>
getOtherDetails(information.id).pipe(
map((detail) => ({ detail, id: information.id })),
),
),
)
}),
)
combinedResults.subscribe((combinedDetails) => {
for (const information of combinedDetails) {
const { detail, id } = information
console.log('现在你可以访问详情和ID')
}
})
乍看之下可能会有点复杂,但它反映了你想要以"rxjs方式"完成的任务。
- 获取通用的"informations"信息。
- 使用mergeMap处理结果并映射到新的可观察对象。
- 使用ForkJoin组合"detail"可观察对象数组。
- 使用"map"管道将每个"detail"响应与原始响应进行映射。
- 仅订阅一次以读取合并后的值。
这种方式使你可以轻松在Angular模板中使用"async"管道显示结果。因此,如果你的目标是显示合并后的值,你完全不必在组件逻辑中订阅,这是一个优势,因为你不必考虑性能或内存泄漏。阅读这里了解更多信息:
<div *ngFor="let item of (combinedResults | async)">{{item | json}}</div>
英文:
You can lean way harder on rxjs
and its pipe operators.
Consider following code:
const combinedResults = getInformations().pipe(
mergeMap((informations) => {
return forkJoin(
informations.map((information) =>
getOtherDetails(information.id).pipe(
map((detail) => ({ detail, id: information.id })),
),
),
)
}),
)
combinedResults.subscribe((combinedDetails) => {
for (const information of combinedDetails) {
const { detail, id } = information
console.log('Now you have access to the detail and the id')
}
})
It might look a bit crazy on first glance, but it reflects what you want to do in an "rxjs way".
- fetch the general "informations"
- use mergeMap to use the result and map to a new observable
- use Forkjoin to combine the array of
detail
observables - Map each
detail
response with the original response using themap
pipe - Subscribe only once to read the combined values
This way enables you to easily display the result in an angular template using the async
pipe. So if your goal is to display the combined values, you don't have to subscribe in your component logic at all, which is a gain because you don't have to think about performance or memory leaks. Read this for more info
<div *ngFor="let item of (combinedResults | async)">{{item | json}}</div>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论