0

I need to obtain a series of data for each of the dates of origin that I have in an array

array

and I need to obtain them in order so I use concatMap to go through my observable of dates and when I obtain the first group of values everything goes fine

of(...this.etiquetasEjeX)
  .pipe(
    concatMap(item=>
        this.dataService.getGastadoRealizadoEnMesYAño(this.proyectoId,
        getMonthNumber(item.slice(0,item.length-4)),
        +item.slice(-4),
        this.acumular)
        ),
    toArray()
  )
  .subscribe(item=>{
    this.gastadoRealizado=item;
    console.log('this.gastadoRealizado: ', this.gastadoRealizado);
  });

I have my this.gastoRealizado array

gastorealizado

but I need to do 3 More calls to the backend to obtain a total of 4 arrays that then feed a graph and I don't know how to add more calls in this established order

Any idea, please?

Thanks

kintela
  • 1,283
  • 1
  • 14
  • 32

1 Answers1

1

Not sure if this answers your question (couldn't post a comment to ask for specifics). But assuming the other back end calls also depend only on an element from this.etiquetasEjeX you could use the zip operator. However regarding the usage of concatMap the four request would be performed simultaneously per item. If you were restricted to one api call at a time this solution would need some adjustments.

import { of, zip } from 'rxjs';
import { concatMap, toArray } from 'rxjs/operators';

//...

of(...this.etiquetasEjeX)
  .pipe(
    concatMap(item=>
        zip(
            this.dataService.getGastadoRealizadoEnMesYAño(...),
            this.dataService.getB...,
            this.dataService.getC...,
            this.dataService.getD...,
        ),
    toArray(),
  )
  .subscribe((arr: [ItemA, ItemB, ItemC, ItemD][])=> {
    //...
  });

Edit:

okay in your comment you mentioned that a subsequent request depends on the result of a prior request. Nesting concatMap operations like your initially requested would be a little messy as you can see in the following example:

of(...this.etiquetasEjeX)
  .pipe(
    concatMap(item=>
      this.dataService.getGastadoRealizadoEnMesYAño(...).pipe(
        concatMap(itemA =>
          this.dataService.getB(itemA).pipe(
            concatMap(itemB =>
              this.dataService.getC(itemB).pipe(
                concatMap(itemC =>
                  this.dataService.getD(itemC).pipe(
                    map(itemD =>
                      // every combination of items would be available here
                      this.getGraph(item, itemA, itemB, itemC, itemD)
                    )
                  )
                )
              )
            )
          )
        )
      )
    ),
    toArray(),
  )
  .subscribe(graphs => {
    // graphs array contains elements that were returned by this.getGraph 
  });

But the same operations could also be called sequentially without losing the intermediate results that you will need for feeding your graph:

of(...this.etiquetasEjeX)
  .pipe(
    concatMap(item=> combineLatest(
      of(item),
      this.dataService.getA(item, ...),
    )),
    concatMap(([item, itemA]) =>
      this.dataService.getB(itemA, ...)
combineLatest(
      of([item, itemA]), // it's important that the elements are enclosed inside an array here otherwise the of operator fires twice and doesn't have the proper effect
      ,
    )),
    concatMap(([prevItems, itemB]) => combineLatest(
      of([...prevItems, itemB]),
      this.dataService.getC(itemB, ...),
    )),
    concatMap(([prevItems, itemC]) => combineLatest(
      of([...prevItems, itemC]),
      this.dataService.getD(itemC, ...),
    )),
    map(([item, itemA, itemB, itemC, itemD]) =>
      this.getGraph(item, itemA, itemB, itemC, itemD)
    ),
    toArray(),
  )
  .subscribe(graphs => {
    // update the state of your object eg.
    this.myGraphs = graphs;
  });

And I noticed that you are using toArray. That means your observable won't provide you with any intermediate results until all your api calls have been finished. Depending on the size of the resulting arrays provided by your api the given solution might be quite time and memory consuming.

kruschid
  • 759
  • 4
  • 10
  • thanks @kruschild I have filled my arrays within the subscribe () method but I need to apply a series of logic with them to obtain based on these, other arrrays and to perform this logic I need to access the value of the date and within subscribe I have already lost it and if I try access my arrays outside the function where I calculate them are undefined – kintela Mar 10 '21 at 11:26
  • thank for the specs @kintela, I updated my answer according to the additional information you provided – kruschid Mar 10 '21 at 13:48
  • 1
    thanks, @kruschild this new interesting answer help me to another scenarios but in my case, subsequent request not depends on the result of a prior request. Once I have all the responses zipped and my arrays filled. I need to operate with that arrays to obtain another. These arrays are properties of my component class so accessible from any method but if I try to access from outside the method where I fill them are not yet filled by the asynchronism of the calls to the server, I understand – kintela Mar 10 '21 at 14:57
  • This is my sample code: https://stackblitz.com/edit/rxjsangular-kintela?file=src/app/app.component.ts – kintela Mar 10 '21 at 15:30
  • I'm happy to help you @kintela. Could you create a new question with your new code? I'm sure the community will be provide you with good solution pretty quick here. ;-) – kruschid Mar 10 '21 at 16:02