angularrxjsangular-akita

Rewriting Nested Subscription


My original code is difficult to debug and maintain, so I'm rewriting my code.

Original Code

this.userService.getLocationId(id).subscribe(
  (locationId) => {
    this.userService.getUserParams(locationId).subscribe(
       (params) => {
          // This API would store users to Akita Store
          this.userService.getUsers(params).subscribe()
          // Get data from Akita Store
          this.users$.subscribe(
             (users) => {
                this.users = [...users]
                this.userService.putSomeUserFirst().subscribe(
                  (data) => {
                    if (data === true) {
                       this.users.unshift(user)
                       this.users = [...new Map(this.users.map(agent => [user.id, user])).values()];
                    } 
                  }
                )
             }
          )
       }
    )
  }
)

So basically, I am calling several API and the parameters for the API are base on the previous API result except the last API call. The last API call is about organizing users in a certain order.

Rewrote Code

this.userService.getLocation(id).pipe(

// Don't know which RxJS operator I should be using
  flatMap((locationId) => {
    if (locationId) {
      return this.userService.getUserParams(locationId)
    }
  }),
  flatMap((params) => {
    return this.userService.getUser(params)
  }),
  flatMap(() => {
   return this.users$
  })
).subscribe(
  (users) => {
    this.users = users
  }
)

I have trouble on implementing the last bit of the original nested subscription. Is the rewrote code a right approach? How should I write the remaining part and which RxJS operator should I be using?


Solution

  • Yes, you are taking the right approach. You should always avoid nested subscribe.

    This will be the implementation that you are looking for:

    this.userService.getLocationId(id).pipe(
        switchMap((locationId) => this.userService.getUserParams(locationId)),
        switchMap((params) => this.userService.getUsers(params)),
        switchMap(() => this.users$),
        tap((users) => this.users = [...users]),
        switchMap(() => this.userService.putSomeUserFirst()),
        tap((data) => {
          if (data === true) {
            this.users.unshift(user);
            this.users = [...new Map(this.users.map(agent => [user.id, user])).values()];
          }
        })
      ).subscribe();
    

    Use tap operator when you just want to do something inside the pipeline, like assign the value.

    flatMap operator that you had in the question is bit different from the switchMap operator. In this specific example both will work fine, but generally you will mostly be using switchMap operator.

    Have a look at this blog to understand how different mapping operators work in RxJS: https://blog.angular-university.io/rxjs-higher-order-mapping/