Alexandre Annic
Alexandre Annic

Reputation: 10768

Rxjs operator similar to combineLatest but that trigger if only one emit

In the RxJs doc of combineLatest, there is written:

Be aware that combineLatest will not emit an initial value until each observable emits at least one value.

Is there an operator that works like combineLatest but that emit a value even if only one of the observable emits a value ?

Upvotes: 3

Views: 2747

Answers (1)

Mrk Sef
Mrk Sef

Reputation: 8032

Assuming observables is an array of observables

combineLatest(...observables.map(
  s => s.pipe(
    startWith(null)
  )
)).pipe(
  filter(arr => arr.filter(x => x != null).length > 0)
)

This will emit an array with null in any position where the source observable hasn't emitted yet. It also filters out the very first emission where the entire array is null.


Update based on comment:

The same thing, but perhaps a bit more robust as it allows for a dummy value with a shape that the developer knows.

You can replace 'null' with anything. For example:

combineLatest(...observables.map(
  s => s.pipe(
    startWith({dummy: true})
  )
)).pipe(
  filter(arr => arr.filter(x => !x?.dummy).length > 0)
)

Update to improve performance:

At scale, looping through an array to see if all values are void/dummy values is bad for performance. Since (in this case) we only want to ignore the first emission, this custom operator can do that without checking the values at all.

filterFirst(n) will ignore the first n emmissions:

function filterFirst<T>(n: number): MonoTypeOperatorFunction<T>{
  return s => defer(() => {
    let count = 0;
    return s.pipe(
      filter(_ => n <= count++)
    )
  });
}

Then you can use it as follows:

combineLatest(...observables.map(
  s => s.pipe(
    startWith(null)
  )
)).pipe(
  filterFirst(1)
)

Upvotes: 4

Related Questions