3
votes

I am using using RxJS (version 6+) to switchMap to another observable which needs to be emitting one value to start the pipe(line).

The current solution is using:

const saveAction$ = new Observable(observer => {
            observer.next();
        }).pipe(switchMap(() => this.saveAction()))

which creates an observable that immediately emits. The this.saveAction() returns an Observable<void> which contains some synchronous code and a return of a call to the HttpClient. The saveAction$ is being saved in a redux store which will be called when another component dispatches the handleSaveChanges.

Another option would be using of(true), but the true has no value here, because it would not be used. Using a Subject would be another option, but the overhead of a subject is not needed, because I need it to only emit once and then complete.

There is an EMPTY (https://rxjs-dev.firebaseapp.com/api/index/const/EMPTY) constant that gives an observable that completes, but never emits. It would be much nicer if there was something like ONCE, instead of using the constructor.

Are there other ways, which are cleaner, to solve this problem?

5
Are your store events available as an observable source? Wouldn’t that be the proper trigger for this observable? saveChanges$ = this.reduxStore.events$.pipe(filter(e => e.type === 'handleSaveAction'), switchMapTo(this.saveAction)) - BizzyBob

5 Answers

1
votes

The EMPTY definition is single line

export const EMPTY = new Observable<never>(subscriber => subscriber.complete());

You could try to define your own ONCE as following

export const ONCE = new Observable<any>(subscriber => subscriber.next());

It could then be used as

ONCE.pipe(switchMapTo(this.saveAction()))

which essentially is the same as your solution.

1
votes

The problematic is, what should ONCE emit for a value? This value has to be provided. That the consumer doesn't need that value is a different story, but the source has to provide something if it calls next().

Due to this fact, the shortest way to write this IMHO would be (as you already mentioned)

of(true)

If you need some more values, where the values and / or count is already known beforehand I would go with something like

from([true, false, true, false])

Or using something to create the array beforehand (loops, LINQ, etc) and using this.

Only when you're going to need to emit values at runtime from something not-observable I would take some kind of Subject.

1
votes

To comment your thought

Another option would be using of(true), but the true has no value here, because it would not be used

When you observer.next(), it in-fact does get a value too - undefined. It is also not used. So based on this, option with of(null) is a simple and straightforward way. I don't find this confusing and used this approach quite few times.

0
votes

Use

Observable2 = Observable1.pipe(first())

The Observable will only emits the first value then close itself. Here is the doc.

0
votes

If I understand your need correctly:

If this.saveAction() already returns an observable, why not return it instead of the new Observable(), and use take(1) (docs) so it'll emit only once?

Something like that:

const saveAction$ = () => this.saveAction().pipe(take(1))

You can then use this.saveAction() subscription wherever is needed:

saveAction$.subscribe()