1

I have two functions inside ngOnInit.

ngOnInit(): void {

    this.getUserInfo();
    this.getUserFeed();
    
  }

I want to run the first function first and use the value of first in order to execute second.

public getUserInfo() {
    return this.userFeedService.getShowdaUserInfo().subscribe(
      (response: any) => {
        this.userId = response.id;
      });
  }

 
  public getUserFeed() {
    return this.userFeedService.getShowdaUserFeed(this.params, this.userId).subscribe(
      (response: any) => {
        this.post = response.data.posts.data;
      });
But it seems that they are running parallelly. I need to retrieve userId from first function in order to get the user feed from the second. The userId is required for a get request in the second function. What should I do?
R. Richards
  • 24,603
  • 10
  • 64
  • 64
Kapilfreeman
  • 897
  • 9
  • 10

2 Answers2

2

If the second request depends upon data in the response from first request. then you need to use one of the band rxjs operator concatMap, mergeMap, switchMap. In this case switchMap works better because it unsubscribes from previous request.

So you have to modify your code like so:

public getUserInfo() {
    return this.userFeedService.getShowdaUserInfo().pipe(
      map((response) => response.id)
    )
  }

 
  public getUserFeed(userId: number) {
    return this.userFeedService.getShowdaUserFeed(this.params, userId)
 }
 
 ngOnInit() {
  this.getUserInfo().pipe(
  switchMap(userId => getUserFeed(userId))
  )
  .subscribe(// your logic here )

}

angular-multiple-http-requests-with-rxjs

  • Thank you for your suggestion. I tried the method but got an error. I will surely try to implement this method as it is good but my problem solved from another answer. Anyway, I knew a lot from your answer and from the above link. Thanks. – Kapilfreeman Apr 20 '21 at 20:53
  • Accepted answer solved your problem but what is matter how is it solved. Using subsribe inside subsribe it is not a good practice you had better use rxjs operators that was built for –  Apr 20 '21 at 21:39
  • Yeah, I will try to implement rxjs operators. I got error implementing the logic you provided. – Kapilfreeman Apr 20 '21 at 22:19
  • I think the mergMap is the dedicated operator for this use case if the second API call depends on sin the first, nice try and nice answer,But i recommend using mergMap instead – Rebai Ahmed Apr 21 '21 at 08:22
2

The functions look like running in parallel because they have Observables inside it, so they run asynchronously. You can find some explanation about asynchronous and synchronous functions here.

In short, in a synchronous code, a task needs to be finished before moving the another one, while in asynchronous code, your running code can move to another task before the execution of a task has ended.

To guarantee that a task B will be executed after an asynchronous task A, you need to explicitly indicate that B will be executed after task A has finished.

When you subscribe to a task, the code inside the subscribe method will be executed after the conclusion of the respective task.

So, one way to accomplish your goal is returning an Observable in both getUserInfo() and getUserFeed() functions. After that, on ngOnInit() you can call getUserFeed() inside the subscribe block of getUserInfo():

ngOnInit(): void {
   this.getUserInfo().subscribe(
      (response: any) => {
          this.userId = response.id;

          // getUserFeed() called inside the subscribe block
          this.getUserFeed().subscribe(
               (response: any) => {
                  this.post = response.data.posts.data;
               }
          );
       }
   );  
}
    
public getUserInfo() {
   return this.userFeedService.getShowdaUserInfo();
}
     
public getUserFeed() {
   return this.userFeedService.getShowdaUserFeed(this.params, this.userId);
}

This approach is just to understand that if your logic needs some asynchronous tasks to be executed in some order, so you need to chain them. But, as apponted by Yuriy, it is not a good practice. A better way is to use rxjs methods like switchMap or mergeMap, like Yuriy also did, which is a bit more advanced:

ngOnInit(): void {
    this.getUserInfo()
        .pipe(switchMap((response: any) => this.getUserFeed(response.id))
        .subscribe((response: any) => this.post = response.data.posts.data))
}
     
 public getUserInfo() {
    return this.userFeedService.getShowdaUserInfo();
 }
 
 public getUserFeed(id: any) {
    return this.userFeedService.getShowdaUserFeed(this.params, id);
 }

P.S. If you are not familiar with rxjs library, switchMap is a rxjs Pipeable Operator. A Pipeable Operator is an operator called using a syntax like observable1.pipe(operator()). A Pipeable Operator doesn't change the original observable, but return a new one after some transformation based on the first one. More details at the Rxjs page.

Jideão Filho
  • 85
  • 1
  • 7
  • You're welcome. I've just edited the answer to use rxjs, as suggested by Yuriy. At the first answer, I was only focused in showing you the need to finish the first asynchronous task before moving to the next one. – Jideão Filho Apr 21 '21 at 00:58