How can I define the execution order of subscription to Observables in Angular?

angular observable/subscribe
angular 8 observable example
angular 6 observable example
angular observable map
return value from subscribe angular 6
angular observable pipe
angular observable vs promise
angular async observable

I have got some get-Calls from an API in a service.ts file:

//code
getCars()
{
this.car = this.http.get(car_url)
return this.car;
}

getTires()
{
this.tires = this.http.get(tires_url)
return this.tires;
}

getSeats()
{
this.seats = this.http.get(seats_url)
return this.seats;
}

In the detail.compoenent.ts I filter this data to a selected car and then present it through detail.component.html - detail.compoenent.ts looks like this:

//code
ngOnInit() 
{
this.Service.getCars()
.subscribe(cars => this.car = cars.find(/*code (selecting car with certain car_id*/)

this.Service.getTires()
.subscribe(tires => {this.tires = tires.filter(/*code (selecting all the tires with the selected car_id*/)}

this.Service.getSeats()
.subscribe(seats => {this.seats = seats.filter(/*code (selecting all the seats with the selected car_id*/)}
}

To filter for the tires and seats, getCar() has to be executed first because information of it is needed to filter for the tires and seats. So how do I have to put the code to ensure this.Service.getCars()subscribe(/code/) is executed before this.Service.getTires().subscribe(/code/) and this.Service.getSeats().subscribe(/code/)?

Simple solution could be make subsequent http requests in call back functions.

ngOnInit() 
{
    this.Service.getCars().subscribe(
      cars => {
           this.car = cars.find();
           this.Service.getTires().subscribe(tires => {
              this.tires = tires.filter();
              this.Service.getSeats().subscribe(
                seats => {this.seats = 
              seats.filter(/*code (selecting all 
                 the seats with the selected car_id*/)}
            }
         }

      });

}

until all data is getting loaded you can show spinner to end user.

Using observables to pass values, As a publisher, you create an Observable instance that defines a subscriber function. const sequence = new Observable(sequenceSubscriber); // execute the� In order to work with Observables we must import the Observable and Subscription classes from the rxjs library at the top of the app.component.ts typescript file. import { Observable, Subscription

Having nested subscriptions is a big code smell. If you feel the need to have a nested subscription, please search higher order observables like switchMap, concatMap, mergeMap, zip, etc.

I took the time to build a full demo of what you're trying to achieve.

First, let's start by defining our interfaces to have some type safety:

export interface Car {
  id: string;
  seatId: string;
  tyreId: string;
}

export type CarResolved = Omit<Car, "seatId" | "tyreId"> & {
  seat: Seat;
  tyre: Tyre;
};

export interface Tyre {
  id: string;
  diameter: number;
}

export interface Seat {
  id: string;
  width: number;
}

Now that we know what kind of data structure we want, let's build a service and return mock data that you can later on replace with your backend:

@Injectable()
export class ResourcesService {
  public getCars(): Observable<CarResolved[]> {
    return this._getCars().pipe(
      switchMap(cars =>
        forkJoin(
          ...cars.map(car =>
            forkJoin(
              this._getTyreById(car.tyreId),
              this._getSeatById(car.seatId)
            ).pipe(
              map(([tyre, seat]) => ({
                id: car.id,
                seat,
                tyre
              }))
            )
          )
        )
      )
    );
  }

  private _getCars(): Observable<Car[]> {
    return of(mockCars);
  }

  private _getTyreById(id: string): Observable<Tyre> {
    return of(mockTyres.find(tyre => tyre.id === id));
  }

  private _getSeatById(id: string): Observable<Seat> {
    return of(mockSeats.find(seat => seat.id === id));
  }
}

const mockCars: Car[] = [
  { id: "car-1", seatId: "seat-1", tyreId: "tyre-1" },
  { id: "car-2", seatId: "seat-2", tyreId: "tyre-2" },
  { id: "car-3", seatId: "seat-1", tyreId: "tyre-3" }
];

const mockTyres: Tyre[] = [
  { id: "tyre-1", diameter: 80 },
  { id: "tyre-2", diameter: 60 },
  { id: "tyre-3", diameter: 75 }
];

const mockSeats: Seat[] = [
  { id: "seat-1", width: 10 },
  { id: "seat-2", width: 20 },
  { id: "seat-3", width: 30 }
];

If you look at the getCars method, it has absolutely no subscribe. It just returns an observable that someone can later on subscribe to. Everything is done through streams :)

Finally, the easiest part: The view.

@Component({
  selector: "my-app",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent {
  public cars$: Observable<CarResolved[]> = this.resourcesService.getCars();

  constructor(private resourcesService: ResourcesService) {}
}

Notice here too: No subscription at all! :)

And the HTML side:

<pre>{{ cars$ | async | json }}</pre>

Our view now shows our array of resolved cars:

[
  {
    "id": "car-1",
    "seat": {
      "id": "seat-1",
      "width": 10
    },
    "tyre": {
      "id": "tyre-1",
      "diameter": 80
    }
  },
  {
    "id": "car-2",
    "seat": {
      "id": "seat-2",
      "width": 20
    },
    "tyre": {
      "id": "tyre-2",
      "diameter": 60
    }
  },
  {
    "id": "car-3",
    "seat": {
      "id": "seat-1",
      "width": 10
    },
    "tyre": {
      "id": "tyre-3",
      "diameter": 75
    }
  }
]

Here's a live demo on stackblitz: https://stackblitz.com/edit/angular-qypary

Observables in Angular, You can define custom events that send observable output data from a child to a and subscribe to them in order to make decisions based on the sequence of� Observables provide support for passing messages between publisher and subscription in our application. The observables can deliver the multiple values of any type like literal, messages, depending on the content. It is an object that defines the callback method to handle the three types of notification that an observable can send.

You can use mergeMap() and subsequently call other observables, you can use the data of the other previous API in another.

this.Service.getCars().pipe(mergeMap((cars) => {
    // cars is available for the other two
    return this.Service.getTires().pipe(mergeMap((tierData) => {
        // tierData is avaibale for getting seats.
        return this.Service.getSeats()
    }))
})).subscribe((seats) => {

})

You can merge the other two in the first pipe itself.

Observables compared to other techniques, Observables are not executed until a consumer subscribes. The subscribe() executes the defined behavior once, and it can be called again. Each subscription� As we can see in our template, the async pipe automatically subscribes and handles our Observables. The pros to this are we can let Angular do the heavy lifting to our Observables. The cons are that this syntax become a bit more complicated when we have multiple subscriptions. In a later step, we will see how we can improve on this. ForkJoin

Understanding, creating and subscribing to observables in Angular , Since each execution is run for every subscriber it's important to not keep subscriptions open for subscribers that don't need data anymore, as that� Angular uses Observable to treat asynchronous code. The same way that we use callbacks and promises in vanilla JavaScript. In fact, the Observable will be added to future versions of JavaScript, but until that happens it is implemented in Angular with the help of the RxJS library. Observables are widely used in Angular for handling asynchronous

A quick tutorial on how to create an observable in Angular 9, The concept of Observables in Angular is extremely useful. In other words you define a function for publishing values, but it is not executed until a consumer In order to work with Observables we must import the Observable and Now we need to define a subscription object of type “Subscription” which� Angular 6 Observables Example Tutorial is the today’s leading topic. Observables are the collections of multiple values over time. Observables are lazy. Angular uses observables extensively in the event system and the HTTP service. Observables are very helpful in asynchronous actions. If you compare observables with promises, then there is a

Subscribing to and Unsubscribing from Observables, Observables are declarative and we can define a function for To execute the observable, we have to create and begin receiving To do this, you call its subscribe() method, passing an observer. Subscribing to Observables in Angular subscribe: ' + num); }, complete() { console.log('1st sequence� Subscribing to multiple observables – Combining observables. This is another type that used to subscribe to multiple observable but in this type, no observable will be a dependent of another observable. But in some cases, may be orders of the subscription is required for the proper execution as planned.

Comments
  • Use concatMap()
  • Or you can use the zip operator, or the swtichMap / mergeMap operators
  • forkJoin() aswell
  • rxjs-dev.firebaseapp.com/api/operators/concatMap
  • I'll write a proper answer tomorrow as I don't have time today.
  • This works fine :-) - I just wonder if it is a clean solution - because if I would add more get-Calls in the future I fear it to get a bit messy in the code
  • Yes, please avoid having nested subscribe. You'll loose control over your inner streams, which could cause memory leaks. I've made a detailed answer to explain in detail
  • Wow - thank you really very much for this detailled answer - the problem is I can't do the filtering in the service.ts since I have another subscribed component that needs the unfiltered data - it is very helpful though :)
  • I did my best based on your question and the provided details but I'm sure you'll be able to adapt it to your need
  • Yes - you did provide a very helpful basis indeed :) - thanks a lot
  • Thank you for this idea :) - I just wonder if this would make the code a bit messy since it all has to be nested inside each other - furthermore i am not sure where you would do the filtering