11

I have an array of observables which I'm executing in parallel using:

let observables: Observable<any>[]

Observable.forkJoin(observables) 

This works perfectly, however, I need to execute the array of observables sequentially, and only emit one result if the last observable has been completed. That's when I tried to use

Observable.concat(observables)

But this returns multiple results, and not only one - combined - result which I get when using forkJoin. So I actually need a combination of the two.

I've tried to use the reduce functionality to execute them sequentially, like this:

return observables.reduce((previous, current) => {
  return previous.flatMap(() => current);
}, Observable.empty());

But with this solution the observables are not executed at all.

Jota.Toledo
  • 27,293
  • 11
  • 59
  • 73
Tom van Brienen
  • 113
  • 1
  • 7
  • 3
    Your reduce-code would work if you didn't use empty() as the first one. Use Observable.of(null), for example. You need to start the chain by emitting one event. – JB Nizet Jan 20 '18 at 17:44

3 Answers3

9

Assuming that your observables emit singular values, not arrays, you could rework your current approach to something like:

return Observable.concat(...observables).reduce((acc, current) => [...acc, current], []);

or even shorter:

return Observable.concat(...observables).toArray();

In the case that they emit array values, you could do the following:

const source = Observable.concat(...observables).flatMap(list => list).toArray();
Jota.Toledo
  • 27,293
  • 11
  • 59
  • 73
9

As Jota.Toledo or Mateusz Witkowski showed in their answers, with the new syntax of RxJS you can do:

return concat(...observables).pipe(toArray());
riorudo
  • 977
  • 7
  • 14
5

You can you use toArray() operator:

Observable.concat(observables).toArray().subscribe()

As stated in RxJS documentation: it creates "an observable sequence containing a single element with a list containing all the elements of the source sequence".

Mateusz Witkowski
  • 1,646
  • 10
  • 24
  • @TomvanBrienen no. It transorms an observable of elements into an observable of array of elements. The unique array emittied by the result observable contains all the elements emitted by the source observable. – JB Nizet Jan 20 '18 at 17:58
  • 1
    should be `Observable.concat(...observables).toArray().subscribe()` – Michael Kang Jan 20 '18 at 18:06
  • @pixelbits You don't need to unwrap the array for `concat`. Both will work. – martin Jan 20 '18 at 18:13
  • 1
    @martin No, there is a difference. One emits an array of observables. The other emits an observable of an array. – Michael Kang Jan 20 '18 at 18:15
  • @pixelbits I really doubt it. See this https://github.com/ReactiveX/rxjs/blob/master/src/internal/observable/concat.ts#L9-L16 and this https://github.com/ReactiveX/rxjs/blob/master/src/internal/observable/concat.ts#L111. Otherwise give me a demo where `concat` behaves differently when used like `concat([a, b, c])` and `concat(...[a, b, c])`. – martin Jan 20 '18 at 18:25
  • 3
    @martin you're referring to the simple case of an observable stream of values. In the ops example, the source is an array of observables. You should try it yourself:) – Michael Kang Jan 20 '18 at 18:31