There are two observables that may be emiting together or separately: stream1 and stream2.
I need my subscription to fire only if stream2 fires less then 1 second after stream1 does.
Any way to achieve that with RxJS?
There are two observables that may be emiting together or separately: stream1 and stream2.
I need my subscription to fire only if stream2 fires less then 1 second after stream1 does.
Any way to achieve that with RxJS?
You can use a timestamp and withLatestFrom to decide which values to emit
Here I just filter such that only values that meet your condition pass through.
stream2.pipe(
timestamp(),
withLatestFrom(stream1.pipe(
timestamp(),
startWith({timestamp: 0, value: null})
)),
filter(([s2, s1]) => s2.timestamp - s1.timestamp < 1000),
map(([s2, s1]) => ({
stream1: s1.value,
stream2: s2.value
}))
);
The following solution really will only work with "hot" Observables because I'm subscribing to the second Observable only after the first one emits:
const stream1$ = timer(1000);
const stream2$ = timer(800);
stream1$
.pipe(
switchMap(val1 => stream2$.pipe(
timeout(1000),
catchError(() => EMPTY),
map(val2 => [val1, val2]),
))
)
.subscribe(console.log);
timeout() will emit an error when stream2$ doesn't emit in less than 1s and the error is immediatelly caught by catchError and ignored.
To use this solution with cold Observables you can turn them into hot.
const stream1$ = timer(1000);
const stream2$ = timer(1800);
const stream2$Published = stream2$.pipe(publish()) as ConnectableObservable<any>;
const subscription = stream2$Published.connect();
stream1$
.pipe(
switchMap(val1 => stream2$Published.pipe(
timeout(1000),
catchError(() => EMPTY),
map(val2 => [val1, val2]),
))
)
.subscribe(console.log);
Live demo: https://stackblitz.com/edit/rxjs-mqudah?devtoolsheight=60
I think this is a rare case where using publish() is actually useful. Otherwise, a little shorter solution might be like the following that will work with cold Observables as well even though it's not as easy to understand:
const stream1$ = timer(400);
const stream2$ = timer(1200);
combineLatest([
stream1$.pipe(timestamp()),
stream2$.pipe(timestamp()),
]).pipe(
// Check that stream1$ emited first && stream2$ emited less than 1000ms after stream1$.
filter(([val1, val2]) => (val1.timestamp < val2.timestamp && (val2.timestamp - val1.timestamp) < 1000)),
map(([val1, val2]) => [val1.value, val2.value]),
).subscribe(console.log);
Live demo: https://stackblitz.com/edit/rxjs-yfhyyh?devtoolsheight=60
However, it's probably not very obvious what the filter() does from quick glance and has some "cognitive comlexity" :).