Matthew
Matthew

Reputation: 3071

Angluar2 How to Optimize: Observable Calls Multiple Observables?

Requirement: call an observable on remote server to get list of product ids. Call Firebase to get product detail (for each product id).

The following code works but it feels more like a promise implementation than an observable implementation. Note that code samples are simplified and somewhat pseudo code.

In theory, this could be solved by calling one Firebase observable that returns all products e.g. where productid in (35, 68). But I can't find a way to do this in Firebase. Instead I make one call for each product id.

this.predicitionioService.getProductIds()
    .subscribe(
            ids => {
                this.productService.getProduct(ids[0].id).subscribe(product => this.product1 = product);
                this.productService.getProduct(ids[1].id).subscribe(product => this.product2 = product);
            }
    );

flatMap is ballpark what's needed here. But the data returned is for the last product only.

this.predicitionioService.getProductIds()
.flatMap((ids) => this.productService.getProduct(ids[0].id))
.flatMap((ids) => this.productService.getProduct(ids[1].id))
.subscribe(

merge kind of gets me there but also returns product ids in the subscribe next function. And I need products to have an index e.g. product 1, 2, 3. I can't just bind a single list. This seems to work with assigning my own index e.g. i++. But again this gets messy.

this.predicitionioService.getProductIds()
.merge(
    this.productService.getProduct(ids[1].id),
    this.productService.getProduct(ids[2].id)
)
.subscribe(

Is there a better way of implementing this with observable methods?

Upvotes: 1

Views: 717

Answers (2)

Can Nguyen
Can Nguyen

Reputation: 1470

var maxConcurrent = 5;

this.predicitionioService.getProductIds() // => Observable of array of ids, 1 item 
  .mergeMap(ids => Observable.from(ids))   // => Observable of ids
  .mergeMap(id => this.productService.getProduct(id.id), null, maxConcurrent) // => Observable of products
  .subscribe(product => { /* do stuff */ }); // do stuff to each product

Notes:

  • From RxJS 5, flatMap is an alias of mergeMap.
  • If you really need to get products as an array, prepend .subscribe with .toArray()
  • The maxConcurrent parameter is the maximum number of concurrent requests to getProduct. If you need to preserve the order of products (same order as in the original ids array), set this to 1 - but it will be slower. (mergeMap with maxConcurrent = 1 would be equivalent to concatMap)

    Upvotes: 3

  • Dinistro
    Dinistro

    Reputation: 5730

    This should do what you need:

    this.predicitionioService.getProductIds()
        .flatMap((ids) => {
            return Observable.forkJoin(
                ids.map(
                    (id) => this.productService.getProduct(id.id)
                )
            );
        })
        .subscribe((products) => {
            //do stuff
        });
    

    This will return an array with all results of the getProduct();

    Upvotes: 1

    Related Questions