I have two observables one$ and two$;
I want one$ only to fire when two$ has been fired at least once. This I think is essentially the skipUntil operator.
one$.skipUntil(two$).subscribe()
But let's say one$ has fired while two$ hasn't. I want a behaviour where the stream remembers that one$ has fired and will fire at least once, as soon as two$ did.
Cheers
This looks like you can use the zip() operator that emits the nth item only when all its source Observables have emitted nth item:
const one$ = Observable.from(['a1', 'a2', 'a3'], Scheduler.async);
const two$ = Observable.from(['b1'], Scheduler.async);
Observable.zip(one$, two$, (v1, v2) => v1)
.subscribe(val => console.log(val));
I'm adding Scheduler.async only to simulate asynchronous behavior (for more info see combineLatest behaviour in Rxjs 5?)
This will print to console:
a1
This is ok only if you know the one$ will emit only once.
Eventually you can use combineLatest() that needs all its source Observables to emit at least one item and then emits on any emission where you can ignore two$ with a selector function.
const one$ = Observable.from(['a1', 'a2', 'a3'], Scheduler.async);
const two$ = Observable.from(['b1', 'b2'], Scheduler.async);
Observable.combineLatest(one$, two$.take(1), (v1, v2) => v1)
.subscribe(val => console.log(val));
We know that we only want the first item from two$, the rest can be ignored.
This will print to console:
a1
a2
a3
CombineLatest does what you want. You only need to provide a projector function:
one$.combineLatest(two$, v1 => v1).subscribe(x => console.log(x));
combineLatest waits until all observables have emitted at least one value, and then emits the combination of the last values of each observable. In your case, you only want the value from one observable. That's the reason to use the projector function. This projector function receives each observable value in one param and returns the value that will be emitted.
See an example in CodePen: https://codepen.io/sanzante/pen/vjMKwg
Also, check combineLatest behaviour in RxMarbles.
And the official documentation for combineLatest.
combineLatest is exactly what you want. This operator will not emit an initial value until each observable emits at least one value.
combineLatest(([one$, two$])).subscribe(([one, two]) => {
// do what you want with those values (or value one as you wish)
})
Related
how can i subscribe for example three observables, and to emit when one of them emit new value depend on the order they are, like forkJoin but emit but the order of them is important so if ob3 emit first, i want the value of obv1 to be null, also obv2
and only obv3 that was emitted will have value
for example
forkJoin(obs1,obs2,ob3)
.subscribe([ob1v,ob2v,ob3v]=>{
if (obv1v){ 'do somthing'}
if (obv2v){ 'do somthing'}
if (obv3v){ 'do somthing'}
})
thanks
Maybe combineLatest with an initial value would work for you. combineLatest will emit the latest value from each source observable whenever any of its sources emit. However, it doesn't emit for the first time until all sources have emitted at least one value.
We can use startWith to overcome this by providing an initial value for each source.
combineLatest([
obs1.pipe(startWith(null)),
obs2.pipe(startWith(null)),
obs3.pipe(startWith(null)),
])
.pipe(
skip(1) // prevent emitting initial [null, null, null] value
)
.subscribe(([v1, v2, v3]) => {
// do something here
});
You can see the output in this StackBlitz.
It seems that you want to do different thing for every observable. Maybe you shouldn't gorup them? If you want to group them and do different side effect for every one of them you can do something similar to BizzyBob anwer but instead of having if statements in subscribe use tap() operator for every stream. Something like this:
combineLatest([
obs1.pipe(tap(() => 'do somthing'),
obs2.pipe(tap(() => 'do somthing')),
obs3.pipe(tap(() => 'do somthing')),
])
.subscribe(([v1, v2, v3]) => {
});
Good practise is not to use subscribe method but instead set this stream to some property in component and than use async pipe in the template.
I have a stream of emissions conforming to: Observable<Notice[]>. Each Notice has a property, isVisible$ (Observable<boolean>) that determines whether or not it is on screen in this particular moment. I want to filter this array of notices by whether the most recent value of isVisible$ is true. When a new array of notices occurs, I want to begin the process again. I know this entails using switchMap on the higher order observable stream.
Neither types of observable will ever complete, so using operators like toArray() will not work here. Each isVisible$ stream is guaranteed to emit at least once.
I want the output to also be of Observable<Notice[]>, emitting each time the isVisible$ stream of any of the inner observable predicates updates.
What I have so far does emit the proper values, but the inner pipeline just groups notices together and emits them (via scan, in lieu of toArray), it doesn't buffer to the length of from(notices) and then emit (if that makes sense). This makes the end result of the stream is too busy.
notices.pipe(
switchMap(notices => from(notices).pipe(
mergeMap(notice => notice.isVisible$.pipe(
map(isVisible => ({ notice, isVisible }))
)),
filter(({ isVisible }) => isVisible),
map(({ notice }) => notice),
scan((noticesArr, noticeBeingAddedOrRemoved) => {
if (!noticesArr.find(n => n.identifier === noticeBeingAddedOrRemoved.id)) {
noticesArr.push(noticeBeingAddedOrRemoved);
}
return noticesArr;
}, [])
))
);
Here's a reproducible sample of what I'm working with on StackBlitz.
I've changed it to use zip, which will only emit when each of the isVisible$ observables emit. You could also use combineLatest if you want to emit whenever any of the source observables emit, rathern than waiting for all of them.
Is there way to emit only if previous value is true and do it in a serial fashion?
For example (but not using race):
race(this.firstObservable$, this.secondObservable$).subscribe(
//do Something
);
But I don't want secondObservable to be called if firstObservable returns false.
You can filter the first value of the observable, and if the value is true, then switchMapTo the second stream:
this.firstObservable$.pipe(
filter(v => v === true),
switchMapTo(this.secondObservable$)
);
You'll need some variant of mergeMap to handle it. #eliya-cohen's answer is one option, an alternative would be to do:
this.firstObservable.pipe(
// If the source will only ever return one value then this is not necessary
// but this will stop the first observable after the first value
take(1),
// Flattens to an empty observable if the value is not truthy
flatMap(x => iif(() => x, this.secondObservable$))
)
Basically you can use filter operator and then use one of the high-order observable operator switchMap (unsub from the previous inner observable), mergeMap (keep previous inner observable sub), etc. (depending on your needs)
this.firstObservable$.pipe(
filter(Boolean),
// be carefull here and instead switchMapTo use lazy alternative - switchMap
switchMap(() => this.secondObservable$)
);
I have a side effect Observable that's required to be resolved before the main Observable is completed. If it were a synchronous operation, I could use the tap() operator. Is there a clear equivalent for an asynchronous operation?
In the following example, I have to map the inner value back to the outer value I actually want to pipe through. How would I avoid this mapping?
const user = requestUser().pipe(
switchMap(user => {
return requestAndAssignToken(user)
.pipe(
map(token => user)
);
})
);
If I understand correctly, you want to ignore the result of the inner Observable and just have the outer Observable wait for it to emit before moving on.
In that case, I'd suggest the delayWhen operator. It is passed a delayDurationSelector that returns an Observable (the duration Observable). It then behaves like stated in the documentation:
The source value is emitted on the output Observable only when the duration Observable emits a value or completes
For your example, it would look like this:
const user = requestUser().pipe(
delayWhen(user => requestAndAssignToken(user))
);
Here is a simple example
I am looking for an operator that would help me pace the results emitted from an observable, it would look like this :
[--A-BC--D-E----------------]
[--A----B----C----D----E----]
I tried AuditTime() but it does not replay the results that was emitted between intervals, it does something like this :
[--A-BC--D-E----------------]
[--A----C----E--------------]
Thanks for your help.
I think this should do what you need:
const e1 = cold('--A-BC--D-E----------------|');
const expected = '--A----B----C----D----E----|';
const source = e1.pipe(
concatMap(val => of(false).pipe(
delay(5, scheduler),
takeWhile(Boolean),
startWith(val),
)),
);
expectObservable(source).toBe(expected);
The trick here is that I'm using concatMap to always wait until the previous Observable completes. The inner Observable emits the value and then postpones its own completion and thus concatMap enforces the delay between two emissions.
See live demo: https://stackblitz.com/edit/rxjs6-test-scheduler?file=index.ts