Search code examples
rxjsrxjs5redux-observablerxjs6rxjs-pipeable-operators

Emit actions before `toArray` - Redux Observable


I'm using Redux Observable and need to solve a timing issue when firing off actions from epics.

I have an array of items that I want to loop over for making AJAX calls on each one. Immediately after receiving the AJAX response, I want to fire off some actions. After all of the AJAX responses come back for each item in the original array, I want to fire off more actions.

How can I get these actions to fire immediately after the timer expires even though the original array hasn't finished looping?

const someEpic = action$ => (
    action$
    .pipe(
        ofType(SOME_ACTION),
        switchMap(({ payload }) => (
            from(payload) // This is an array of items
            .pipe(
                mergeMap(() => (
                    timer(5000) // This is my AJAX call
                    .pipe(
                        map(loadedAction),
                    )
                )),
                toArray(),
                map(anotherAction),
            )
        ))
    )
)

Solution

  • Probably the easiest way is actually emitting actions using tap wherever you want. This is assuming you have access to the store. For example:

    tap(result => this.store.dispatch(...))
    

    However a more "Rx" way would be splitting the chain using multicast and then reemitting one part immediatelly (that's the loading progress) and the other half chaining with toArray() to collect all results that would be then turn into another action signaling that loading is done.

    import { range, Subject, of } from 'rxjs';
    import { multicast, delay, merge, concatMap, map, toArray } from 'rxjs/operators';
    
    const process = v => of(v).pipe(
      delay(1000),
      map(p => `processed: ${p}`),
    );
    
    range(1, 5)
      .pipe(
        concatMap(v => process(v)),
        multicast(
          () => new Subject(), 
          s => s.pipe(
            merge(s.pipe(toArray()))
          )
        ),
      )
      .subscribe(console.log);
    

    Live demo: https://stackblitz.com/edit/rxjs6-demo-k9hwtu?file=index.ts