I have this simple usecase that I can't seem to figure out.
Given 2 observables, A and B
A starts emitting items after B emits it's first item, and B can continue to emit items, which should no longer affect A.
I need this because A needs the first value emitted by B to start emitting.
I've tried both skipUntil and startWith to no avail.
I think flatMap can help you do what you want:
Observable<Integer> createA(Observable<Integer> B) {
return B.take(1).flatMap(b -> {
/* replace with your Observable A implementation here */
BehaviorSubject<Integer> subjectA = BehaviorSubject.create();
return subjectA;
});
}
This method returns an Observable A, which begins emitting only after it has been initialized with the first value emitted by Observable B.
Related
I have 2 observables A and B which can emit at any time. But only when A emits a new value and then B emits a new value too, I collect these 2 values. If B just emits new values without A emitting new values first, I don't collect any values.
I know concatMap might be useful but it needs the previous observable to complete whereas in my case neither ever completes until everything is destroyed.
This can be modelled as projecting each element of A into the first arriving element of B, discarding any previous subscription to B when a new A arrives, e.g.:
A.pipe(
switchMap(x => B.pipe(
first(),
map(y => ({ a: x, b: y }))
)
)
of([1,2,3]).subscribe(console.log)
prints:[1,2,3]
But:
of([1,2,3]).pipe(concatAll()).subscribe(console.log)
prints:
1
2
3
Why the above happens? Why adding concatAll() emits the elements of the array one by one? Isn't this somehow the opposite of what the word concat means?
I feel that concatAll() acts differently depending on the input.
Consider also this:
from([of(1),of(2),of(3)]).pipe(concatAll()).subscribe(console.log)
It will again print:
1
2
3
So of([1,2,3]).pipe(concatAll()) == from([of(1),of(2),of(3)]).pipe(concatAll())
But of([1,2,3]) != from([of(1),of(2),of(3)]) because subscribing to the latter will print:
Observable { _isScalar: false, _subscribe: [Function] }
Observable { _isScalar: false, _subscribe: [Function] }
Observable { _isScalar: false, _subscribe: [Function] }
The right side of the above equality is pretty clear to me, but where is documented that concatAll() should emit all the values of the array separately, acting like a pipeable from?
All the operators that deal with higher-order observable operators, perform the same conversion that RxJS's from operator does.
that is:
mergeMap(_ => ([1,2,3]))
Is basically the same as
mergeMap(_ => from([1,2,3]))
That works the same when merging/switching/concatenating promises and other iterables.
The difference you're seeing in behaviour is also attributed to the fact that from creates a new observable, while the higher-order observable operators tend to create a new observables and define behaviour around how those observables are subscribed to.
so concatAll is adding extra behaviour.
Finally, just because two things have similar outputs for a given example, doesn't mean they're doing the same thing under the hood.
of([1,2,3]).pipe(concatAll()) and from([of(1),of(2),of(3)]).pipe(concatAll()) describe two very different set of behaviours that in (this case) give you the same output to the console.
The first emits an array and has that array turned into a stream of numbers by concatAll(). The second emits three observables, and has has concatAll() subscribe to each only when the previous one completes. They complete after emitting just one number.
You can see the distinction clearly with your second example:
of([1,2,3]) and from([of(1),of(2),of(3)])
The first emits and array, the second emits three observables. That hasn't changed.
concat([1,2,3]), &&
concat(of(1), of(2), of(3))
Emit the same output again as well.
Here's the scenario: there are two observable streams (A and B). I want to only emit from B after A has emitted at least once.
I tried combineLatest, but the problem with that was that the combined stream emitted when either A or B emitted. I only want this stream to emit when B emits, I do not need the value from A. I just need it to have been emitted once.
This is what I am looking for:
---a---a-- A
-b---b---- B
---b-b---- required
I think this should work:
combineLatest(a$.pipe(first()), b$)
.pipe(
map(([a, b]) => b)
)
combineLatest will emit for the first time when both a$ and b$ would have emitted, then it will only emit when b$ emits, due to a$.pipe(first()).
Once the combine latest emits then switch back to b.
combineLatest(a$, b$).pipe(
map(([a, b]) => b),
switchMap(_ => b$)
);
As title suggest, both function seems to have similar effect and return emit nothing when predicate does not match. It looks like skipWhile is the reverse of filter ?
As #cartant says
skipWhile "... emits all further source items as soon as the condition becomes false"
Note that skipWhile takes a predicate (expression that returns a boolean).
In addition there is also skipUntil, which this takes an observable and values will be skipped until that observable emits anything.
So sometimes the following can be useful (pan is a hammerjs event btw.):
// When event.deltaX reaches 20 emit true to indicate a threshold is reached
const thresholdReached$ = pan$.pipe(map(e => Math.abs(e.deltaX) > 20 ), first(v => v), shareReplay(1));
// Emit all events, but skip them until the first time that thresholdReached$ emits
const panMove$ = pan$.pipe(skipUntil(thresholdReached$));
Important to realize that the observable passed to skipUntil can emit any value to trigger the skipping to stop. It doesn't have to be a 'truthy' value.
So if you have var b = new BehaviorSubject(false) then skipUntil(b) will immediately stop skipping and you will quickly get very confused!
Let's say there are 2 observables, observable A and observable B.
When A emits a value, I want to wait 1 second, and then emit the latest value of B. If A emits another value while waiting 1 second, I want it to forget about the previous value and wait another 1 second (like switchMap does).
How could I achieve such behaviour?
It looks like you could do this easily with the withLatestFrom operator like the following:
const a$ = ...;
const b$ = ...;
a$
.switchMap(v => Observable.of(v).delay(1000))
.withLatestFrom($b.startWith(null), (a, b) => b)
.subscribe(...);