JD Carr
JD Carr

Reputation: 239

How to keep array order in observable when only called randomly?

I have an array of objects that only sometimes require a service call. The objects that require a service call take awhile to fill, so I end up with an array of the objects that didn't require a service call and then the rest at the end.

I've tried async / await, promise, and just regular subscription. Im very shaky with rxjs so I'm not sure if there's a better way to do this with observables and pipable operators

public getComments(cars: any) {
  cars.forEach( async car => {
    if(car.status === '1') {

      // trying async / await
      let repairs = await this.service.getComments(car.carId)
      car.repairs = repairs
      this.carRepairs.push(car)

      // regular subscribe
      // this.service.getComments(car.carId).subscribe(result => {
      //   car.repairs = result
      //   this.carRepairs.push(car)
      // })

      // trying promise
      // this.service.getComments(car.carId).toPromise().then(result => {
      //   car.repairs = result
      // })
    } else {
      this.carRepairs.push(car)
    }
  })
}

If anyone knows of a better way to do this with rxjs and keep the order, I'd love to learn.

Upvotes: 0

Views: 542

Answers (1)

bryan60
bryan60

Reputation: 29335

so first you're gona want to map all of your items into an observable, even if they don't need to be observables, then you join them together:

import {forkJoin, of} from 'rxjs';
import {map} from 'rxjs/operators';


public getComments$(cars: any) {
  const results$ = cars.map( car => { // map the array into observables
    if(car.status === '1') {

      // return service call observable
      return this.service.getComments(car.carId).pipe(
        // ** see note on this **
        map(repairs => Object.assign(car, {repairs})) // add result to car and map to the car
      );

    } else {
      // return mock observable;
      return of(car);
    }
  });

  // return all observables joined together
  return forkJoin(results$); 
}

then you can just call it and subscribe to the result:

this.getComments$(this.cars).subscribe(carRepairs => {
  // do whatever here
  this.carRepairs = carRepairs;
});

this will ensure that the carRepairs are in the same order as cars.. all the needed requests will run in parallel, but you could do this sequentially by using concat instead and slightly modifying your subscribe function, if you want to preserve some kind of cascade effect.

note: I preserved a side effect from your original code where you mutate the objects in your cars array, normally, I wouldn't do that and I'd do Object.assign({}, car, {repairs}) to ensure I don't produce side effects. side effects and mutation are often a source of bugs and should be avoided when ever possible.

Upvotes: 2

Related Questions