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?

- 430
- 1
- 3
- 12
2 Answers
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
}))
);

- 7,557
- 1
- 9
- 21
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" :).

- 93,354
- 25
- 191
- 226