使用JavaScript对象或sourcesObject实现的forkjoin

huangapple go评论115阅读模式
英文:

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 =&gt; {
	    let subs = [];
        for (const information of informations) {
            subs.push(getOtherDetails(information.id));
        }
		forkJoin(subs).subscribe(response =&gt; {
           //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&lt;any&gt;}[] = [];
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 =&gt; {
	  let subs = [];
	  for (const information of informations) {
		 subs.push(getOtherDetails(information.id).pipe(
			map(data =&gt; ({ id: information.id, data })) // &lt;---
		 ));
	  }
	  forkJoin(subs).subscribe(objects =&gt; {           
		 objects.forEach(({id, data}) =&gt; { /* 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 =&gt; {
      let subs = [];
      for (const information of informations) {
         subs.push(getOtherDetails(information.id).pipe(
            map(data =&gt; ({ id: information.id, data })) // &lt;---
         ));
      }
      forkJoin(subs).subscribe(objects =&gt; {           
         objects.forEach(({id, data}) =&gt; { /* 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 =&gt; {
      const subs = informations.map(
         ({id}) =&gt; getOtherDetails(id).pipe(map(data =&gt; ({ id, data })))
      );
      forkJoin(subs).subscribe(responses =&gt; {           
         responses.forEach(({id, data}) =&gt; { /* 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 =&gt; informations.map(
      ({id}) =&gt; getOtherDetails(id).pipe(map(response =&gt; ({ id, data })))
   ),
   switchMap(requests =&gt; forkJoin(requests))
).subscribe(
   responses =&gt; responses.forEach(({id, data}) =&gt; { ... })
);

答案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方式"完成的任务。

  1. 获取通用的"informations"信息。
  2. 使用mergeMap处理结果并映射到新的可观察对象。
  3. 使用ForkJoin组合"detail"可观察对象数组。
  4. 使用"map"管道将每个"detail"响应与原始响应进行映射。
  5. 仅订阅一次以读取合并后的值。

这种方式使你可以轻松在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) =&gt; {
    return forkJoin(
      informations.map((information) =&gt;
        getOtherDetails(information.id).pipe(
          map((detail) =&gt; ({ detail, id: information.id })),
        ),
      ),
    )
  }),
)

combinedResults.subscribe((combinedDetails) =&gt; {
  for (const information of combinedDetails) {
    const { detail, id } = information

    console.log(&#39;Now you have access to the detail and the id&#39;)
  }
})

It might look a bit crazy on first glance, but it reflects what you want to do in an "rxjs way".

  1. fetch the general "informations"
  2. use mergeMap to use the result and map to a new observable
  3. use Forkjoin to combine the array of detail observables
  4. Map each detail response with the original response using the map pipe
  5. 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

&lt;div *ngFor=&quot;let item of (combinedResults | async)&quot;&gt;{{item | json}}&lt;/div&gt;

huangapple
  • 本文由 发表于 2023年6月6日 08:05:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/76410668.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定