Consider the following snippet
const { NEVER, timer } = rxjs;
const { catchError, switchMap, timeout } = rxjs.operators;
timer(0, 3000).pipe(
switchMap(() =>
timer(randomIntFromInterval(1, 4) * 1000).pipe( // <-- mock HTTP call
catchError(() => {
// do something on error
console.log('Caught error');
return NEVER;
})
)
),
).subscribe({
next: (value) => console.log('Next triggred')
});
// credit: https://stackoverflow.com/a/7228322/6513921
function randomIntFromInterval(min, max) {
const value = Math.floor(Math.random() * (max - min + 1) + min);
console.log(`Simulated HTTP call ${value}s`);
return value;
}
.as-console-wrapper { max-height: 100% !important; top: 0px }
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/7.4.0/rxjs.umd.min.js"></script>
Here the catchError
would only be triggered when the HTTP call emits an error. But if the HTTP call doesn't return anything within the 3 seconds poll timer, the previous request would be cancelled before the next call. I'd like to perform error-handling (essentially triggering the catchError
operator) over these cancelled requests.
I'm aware we could pipe in a timeout
with < 3s threshold to throw an error. But I'd like to handle it without using timeout
operator.
Could anyone come up with a better solution? TIA.
I can suggest slightly different approach: instead of throwing an error you can just track such cases and apply the logic you need
Here's an operator to do this:
function switchMapWithOvertakeEvent<T, R>(
project: (value: T, index: number) => ObservableInput<R>,
onOvertake: (value: T) => void
): OperatorFunction<T, R> {
let awaitingResponse = false;
return (src$) =>
src$.pipe(
tap((v) => {
if (awaitingResponse) {
onOvertake(v);
}
awaitingResponse = true;
}),
switchMap(project),
tap(() => (awaitingResponse = false))
);
}
It can be used with your example as follows
timer(0, 3000)
.pipe(
switchMapWithOvertakeEvent(
() =>
timer(randomIntFromInterval(1, 10) * 1000).pipe(
// <-- mock HTTP call
catchError(() => {
// do something on error
console.log('Caught error');
return NEVER;
})
),
() => console.log('http call cancelled')
)
)
.subscribe({
next: (value) => console.log('Next triggred'),
complete: () => console.log('complete'),
});
// credit: https://stackoverflow.com/a/7228322/6513921
function randomIntFromInterval(min, max) {
const value = Math.floor(Math.random() * (max - min + 1) + min);
console.log(`Simulated HTTP call ${value}s`);
return value;
}
You can play with the code here https://stackblitz.com/edit/mjemgq?devtoolsheight=50