The user clicks the first page and then the second page. Due to the different return time of the request, a bug is caused, resulting in the user finally seeing the data on the first page.
I want to discard the incomplete observable
. Is there a corresponding Operator
?
The correct result of the following example should only output 2
import { Observable } from "rxjs";
let time = null;
const o = new Observable((s) => {
setTimeout(() => {
s.next();
}, time);
}).pipe(); // <- I think I should add something
function render (pageNumber) {
o.subscribe(() => {
console.log(`Render page ${pageNumber}`);
});
}
// The user wants to see the first page
time = 2000;
render(1);
// After 1ms...
// The user wants to see the second page
time = 1;
render(2);
// Due to the different return time of the request:
// 1. the first page returns in 2 seconds
// 2. the econd page returns in 1ms
// As a result, the second page is rendered first, and then the first page is rendered. The user finally sees the first page.This is wrong and the user should see the second page.
CodePudding user response:
I suppose timer is something that you assign randomly to simulate api request time. The following pattern is better, when next page request comes in, with switchMap
it'll cancel the previous one
render
.pipe(switchMap((obj: any) => timer(obj.time).pipe(mapTo(obj.page))))
.subscribe(console.log);
render.next({ time: 2000, page: 1 });
//request for page 2 immediately
render.next({ time: 1, page: 2 });
//request for page 3 after 3 second
setTimeout(() => render.next({ time: 1000, page: 3 }), 3000);
In above example you will only see page 2 and 3 print to console. https://stackblitz.com/edit/rxjs-g5e5zf?file=index.ts
CodePudding user response:
Your example is a bit weird. If you set a async action (your timeOut) inside your Observable creation and call next with inside it, your observable will emit based on how long the async action takes. I think you're looking for switchMap in this case.
Example (not tested):
// Create observable
const _a$ = new Subject();
const a$ = _a$.pipe(
// User input so maybe wait a bit first for user to be done clicking
debounceTime(100),
// Then switchMap to cancel previous and switch to the new result you want
switchMap(async x => {
// Do some async page rendering
return new Promise(resolve => setTimeout(() => {
resolve();
}, 2000));
})
);
a$.subscribe();
// The user wants to see the first page
_a$.next(1);
// The user wants to see the second page
_a$.next(2);