简体   繁体   中英

How to merge nested observables in a pipe rxjs

In my Angular app I retrieve a list of post from an API call and, for each fo the posts, I have to do another API call to retrieve the number of likes.
I can't think of a clean way to do it, but current attempt of a solution is the following:

apiCall.pipe(
  switchMap((posts: Post[]) => {
    return posts.map((post: Post) => {
      return this.getPostLikes(post.rootMessage).pipe(
        map((likes: Like[]) => {
          post.rootMessage.reactions = likes;
          return post;
        }),
      );
    });
  }),
  mergeAll(),
  map((res: Post[]) => {
    return { res, currentPage: page + 1 };
  }),
)

I feel like the entire approach is wrong, but I can't think at any other strategies.

A tangent note: making everything an inline anonymous function makes code hard to read. How about more verbose, but easier to maintain style:

private getLikesForPostsArray = (posts: Post[]) => forkJoin({
  posts: of(posts),
  likes: forkJoin(posts.map((post: Post) => this.getPostLikes(post.rootMessage))),
});

private updateReactionsForPosts = ({posts, likes}: { posts: Post[], likes: Like[] }) =>
  posts.map((post: Post, index: number) => ({
    rootMessage: {...post.rootMessage, reactions: likes[index]},
    ...post,
  }));

//...

return apiCall.pipe(
  switchMap(this.getLikesForPostsArray),
  map(this.updateReactionsForPosts),
  map((res: Post[]) => ({
    res,
    currentPage: page + 1 // I don't see where `page` is defined
  })),
);

The solution depends on whether you want to make the requests in parallel or one after another. Your solution right now with mergeAll() will make all requests at the same time but I think you can make it more simple without mergeAll() :

apiCall.pipe(
  switchMap((posts: Post[]) =>
    merge(posts.map(post => this.getPostLikes(post.rootMessage).pipe(
      map(likes => ({ likes, rootMessage: post.rootMessage})), // Just to be able to pair post with a response
    ))).pipe(,
      scan((acc, {likes, rootMessage}) => {
        const post = acc.find(p => p.rootMessage === response.rootMessage);
        post.rootMessage.reactions = likes;
        return acc;
      }, posts),
      last(), // Emit only once when all request have completed
    )
  }),

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM