1

How is it possible with RXJS to make a cascaded forEach loop? Currently, I have 4 observables containing simple string lists, called x1 - x4. What I want to achieve now is to run over all variation and to call a REST-Api with an object of variation data. Usually, I would do something like that with a forEach, but how to do with RXJS? Please see the abstracted code:

    let x1$ = of([1,2]);
    let x2$ = of([a,b,c,d,e,f]);
    let x3$ = of([A,B,C,D,E,F]);
    let x4$ = of([M,N,O,P]);

    x1$.forEach(x1 => {
        x2$.forEach(x2 => {
            x3$.forEach(x3 => {
                x4$.forEach(x4 => {
                    let data = {
                        a: x1,
                        b: x2,
                        c: x3,
                        d: x4
                    }

                    return this.restService.post('/xxxx', data)
                })  
            })  
        })          
    })

Is something like that possible with RXJS in an elegant way?

Lars
  • 920
  • 1
  • 14
  • 34
  • Are your input lists even actually observables? If so, your question is heavily underspecified as it's unclear what behavior you're looking for as their data arrives over time. If not, then don't use RxJs to calculate the combinations. – Ingo Bürk Jun 29 '21 at 05:47
  • yes, the input lists are observables (see the `of()`, was just for the abstraction of the code here). The input values for the lists comes from other REST-API calls. So they arrive just one time. – Lars Jun 29 '21 at 05:57
  • Something like Merge or Zip could be what you want. Multiple Zip, can be written as [And-Then-When](http://introtorx.com/Content/v1.0.10621.0/12_CombiningSequences.html). Note however that the Merge will not combine the values (not like your `data` object) and Zip will stop when the first sequence stops. Overall, I think the forEach loop is already fine if this is really what you want. – mihca Jun 29 '21 at 06:24

1 Answers1

2

Let's assume you have a function combineLists which represent the plain-array version of the logic to turn static lists into an array of request observables:

function combineLists(lists: unknown[][]) {
  const [x1s, x2s, x3s, x4s] = lists;

  // Calculate combinations, you can also use your forEach instead
  const combinations = x1s
    .flatMap(a => x2s
    .flatMap(b => x3s
    .flatMap(c => x4s
    .flatMap(d => ({a, b, c, d})))));
    
  return combinations.map(combination => this.restService.post('/xxxx', combination));
}

Since your input observables are one-offs as well, we can use e.g. forkJoin. This waits for all of them to complete and then runs with their respective plain values. At this point you're back to computing the combinations with your preferred method.

forkJoin([x1$, x2$, x3$, x4$]).pipe(
  map(combineLists),
);

Assuming your REST call is typed to return T, the above produces Observable<Observable<T>[]>. How you proceed from here depends on what data structure you're looking for / how you want to continue working with this. This didn't seem to be part of your question anymore, but I'll give a couple hints nonetheless:

If you want a Observable<T>, you can just add e.g. a mergeAll() operator. This observable will just emit the results of all individual requests after another in whichever order they arrive.

forkJoin([x1$, x2$, x3$, x4$]).pipe(
  map(combineLists),
  mergeAll(),
);

If you want an Observable<T[]> instead, which collects the results into a single emission, you could once again forkJoin the produced array of requests. This also preserves the order.

forkJoin([x1$, x2$, x3$, x4$]).pipe(
  map(combineLists),
  switchMap(forkJoin),
);

Some words of caution:

  • Don't forget to subscribe to make it actually do something.
  • You should make sure to handle errors on all your REST calls. This must happen right at the call itself, not after this entire pipeline, unless you want one single failed request to break the entire pipe.
  • Keep in mind that forkJoin([]) over an empty array doesn't emit anything.
  • Triggering a lot of requests like this probably means the API should be changed (if possible) as the number of requests grows exponentially.
Ingo Bürk
  • 19,263
  • 6
  • 66
  • 100