observable1.pipe(
withLatestFrom(observable2),
.. do something with both emitted observable values
)
Problem with withLatestFrom is that if observable2 hasn't emitted any event before the observable1 did then it's a dead code. How should I modify the code to ensure that do something with both emitted observable values will have have both observable values emitted at least once prior to the call? Maybe some wrapper around the forkJoin?
You could use the RxJS combineLatestWith if you're looking for a pipable operator instead of combineLatest function.
observable1.pipe(
combineLatestWith(observable2),
// ... do something with both emitted observable values
)
Looks like a use case for the combineLatest operator. This operator takes an array of observables and waits for every to emit at least once. Once all the observables have emitted, the operator emits for the first time. After that it emits every time any of the observables emits.
You can enforce a first emission with startWith(), you apply it to both observable 1 and 2 depends on your need.
observable1.pipe(
withLatestFrom(observable2.startWith(null)),
.. do something with both emitted observable values
)
NgRx provides a concatLatestFrom operator implementation you can reference. It wraps withLatestFrom to produce the behavior that you (and very many others) are expecting and is potentially more semantically clear than combineLatestWith. This also works on versions RxJS before 7:
source1$.pipe(
concatLatestFrom(source2$),
...
)
Related
This question is for learning purposes, not to solve a particular problem (please move it to the appropriate section if necessary).
I'm learning about piping operators in the RxJS library. At this site here (https://rxjs.dev/guide/operators) it distinguishes between pipeable operators and creator operators.
It defines pipeable operators as follows:
A Pipeable Operator is a function that takes an Observable as its input and returns another Observable. It is a pure operation: the previous Observable stays unmodified.
And it defines creator operators as follows:
Creation Operators are the other kind of operator, which can be called as standalone functions to create a new Observable. For example: of(1, 2, 3) creates an observable that will emit 1, 2, and 3, one right after another.
But this leaves me wondering: is there such an operator as one that DOES modify the observable it gets as input and returns it as output? I haven't come across anything like that. Is there a reason such an operator doesn't exist? What kind of undesired behavior would result from such an operator?
You can see pipable operation as a series of function execution, in most of the time there's no need for modifying the upstream function. What we interest in is transforming data and add custom operation as we proceed down the stream
fn(fn2(fn3(...)))
if in any case you want to modify upstream behavior, the upstream observable has to be designed to allow such case, for instance use a function factory to let user add an middleware
e.g
const interceptor=()=>{...}
const getUpstreanFn=(middleware)=>(param)=>{ middleware()......}
const upstreamFn=getUpstreamFn(middleware)
I like the readability provided by
observable
.pipe(operator1)
.pipe(operator2)
.pipe(operator3)
.subscribe()
And it reminds me of a chain of thens for a promise.
But I know this is in every documentation example
observable
.pipe(
operator1,
operator2,
operator3
).subscribe()
Is there something lost in doing the first thing over the second? I am probably missing some crucial information here and maybe it's very obvious, actually. Thanks for the help.
certainly they are equal and they have same result.
both of them is equal to
operator3(operator2(operator1(observable))).subscribe()
i am trying to 'think in streams'. What is the correct way to emit a value to stream B after stream A has completed?
my current implementation is as follows:
streamADelete$(data)
.do(() => dismiss())
.subscribe(() => streamB$.next()) // this seems wrong!
to me there is something wrong with this implementation
If your main goal is to call streamB.next(), but somewhere before the subscribe block, then the correct place to do that would be the tap() operator, which is meant for doing side-effects.
streamADelete$(data)
.pipe(
tap(() => streamB$.next()),
// Continue with your next operator
)
This does seem like a strange thing to do though, we are usually able to achieve most things without doing side-effects. I am not exactly sure what your use-case is, but another pattern that might be helpful could be to split your stream into two like this:
streamADelete$.pipe(
// Do whatever you want to do in your original stream here
)
.subscribe();
streamADelete$.pipe(
// Do whatever you wanted to do in you streamB$ stream here
)
.subscribe();
(Your RxJS also seems a little strange, are you using an old version of RxJS / following some old tutorials?)
The documentation isn't helpful enough for me to understand the difference between these.
It's like concatMap, but maps each value always to the same inner
Observable.
http://reactivex.io/rxjs/file/es6/operators/concatMapTo.js.html
I tried checking out learnrxjs.io's examples on stackblitz, but even with those, I wasn't able to immediately identify what the distinguishing feature was separating these.
FYI i saw this other similar question
What is the difference between mergeMap and mergeMapTo?
but the answer in there wasn't satisfactory, because in the learnrxjs.io examples, they clearly map to observables, not hard-coded values.
https://www.learnrxjs.io/operators/transformation/concatmapto.html
If someone could provide some examples (and maybe a brief explanation) to help distinguish the *** vs the ***To higher-order observable operators, I'd appreciate that, thanks.
Simply said, variants with *To will always use the same Observable that needs to be created when the whole chain is created regardless of the values emitted by the chain. They take an Observable as a parameter.
Variants without *To can create and return any Observable only when their source Observable emits. They take a callback as a parameter.
For example when I use mergeMapTo I'm always subscribing to the same Observable:
source.pipe(
mergeMapTo(of(1)),
)
Every emission from source will always be mapped to of(1) and there's no way I can change that.
One the other hand with just mergeMap I can return any Observable I want depending on the value received:
source.pipe(
mergeMap(v => of(v * 2)),
)
Maybe an easier way to think about this is to remember that *To variants map a value to a constant (even when it's not a "real JavaScript constant").
I have a two dimentional array of BehaviorSubject<number>s. For debugging purposes I want to write the values in a formatted manner as soon as all the array cells emit value. So I wrote this:
Observable.zip(universe.map(row => Observable.zip(row)))
.takeUntil(stopper)
.subscribe(u =>
console.log(`[\n\t[${u.map(r => r.toString()).join("],\n\t[")}]\n]`))
Nothing written. And also this hasn't work:
Observable.zip(universe[0])
.takeUntil(stopper)
.subscribe(u => console.log(`1[${u.toString()}]`))
But these following worked (the array has 5 columns):
Observable.zip(universe[0][0], universe[0][1], universe[0][2], universe[0][3], universe[0][4])
.takeUntil(stopper)
.subscribe(u => console.log(`2[${u.toString()}]`))
Observable.zip(Observable.zip(Observable.zip(Observable.zip(universe[0][0], universe[0][1]), universe[0][2]), universe[0][3]), universe[0][4])
.takeUntil(stopper)
.subscribe(u => console.log(`3[${u.toString()}]`))
Also I have considered .zipAll() operator but there is no document about it.
This may be a bug in Observable.zip() code as it shows ArrayLike<BehaviorSubject<number>> as possible argument type in code assistance.
So is there any other way to get this functionality? How can I get the array values written down once all of the values are reassigned, without knowing the actual dimensions of course?
The important thing is that zip() operator doesn't take an array of Observables but an unpacked series of Observables instead.
That's why Observable.zip([obs1, obs2, obs3]) doesn't work.
But Observable.zip(obs1, obs2, obs3) works.
It's not possible to help you when we don't know what universe is. From what you have now it seems like you could use destructuring assignment (assuming you're using ES6 or TypeScript):
Observable.zip(...universe[0]);
I don't know what plans are with zipAll() but right now it just callls zip().
As of rxjs#5.0.3 Observable.zip() function implementation does not recognize Observable arrays even though export declare function zipStatic<T>(array: ObservableInput<T>[]): Observable<T[]>; and export declare function zipStatic<T>(...observables: Array<ObservableInput<T>>): Observable<T[]>; function declarations take place in rxjs/operator.zip.d.ts (What is the difference between this declarations is beyond my Type/Javascript knowledge). It simply pumps the argument object members passed to it to a local array and never flattens them if you pass array. And even does not check parameter types to raise the situation.
After receiving #martin's answer above, I changed the calls to Observable.zip() with Observable.zip.apply(null, observable_array) then the problem is solved. But .zip() should accept (at least one) array of Observables in order to help readability and adhere to aforementioned function declarations.