Typescript/Rxjs - wysyłanie kilku zapytań do tego samego endpointu w czasie filtrowania

0

Mam następujący problem: potrzebuję filtrować listę według określonych kryteriów (konkretnie zaznaczone w dropdownie checkboxy). Problem w tym, że jeśli dwukrotnie szybko wykonamy requesty (np. zaznaczymy opcję 'zaznacz wszystkie pozycje', a następnie szybko odklikamy), w bardzo podobnym czasie pójdą dwa zapytania do tej samej metody z api. I zdarza się, że w pierwszej kolejności zostaje zwrócona odpowiedź dla drugiego z tych requestów (więc ta oczekiwana), a następnie zostaje nadpisana przez pierwszą, nieaktualną. Finalnie, mimo że np. mamy wyfiltrowaną tylko jedną pozycję na liście, pojawią się wszystkie.

Metoda wygląda następująco:

 private search(): void {
        const searchCriteria = this.prepareSearchCriteria();
        this.service.search(searchCriteria, false).subscribe(x => {
            if(result) {
                this.items = result.items; 
            }
        });
    }

Pytanie, w jaki sposób zakolejkować, żeby odpowiedzi przychodziły w tej kolejności, w jakiej zostały wysłane i czy da się to zrobić na poziomie frontendu. Ewentualnie być może można by po każdym następnym wejściu do metody anulować poprzednią subskrypcję i rozpocząć następną. Pytanie, co byłoby tutaj najbardziej poprawne. Jak ktoś ma jakieś doświadczenie w podobnym temacie, prosiłbym o pomoc.

1

Poczytaj o operatorze switchMap(). Dzieki niemu w przypadku żądania http poprzednie zostaną automatycznie anulowane.

1

https://www.learnrxjs.io/operators/filtering/debouncetime.html

Jeżeli nie zależy Ci tak bardzo na tym żeby wysyłać dwa requesty w tak szybkim odstępie czasu to rozważ operator z powyższego linka.

0

Finalnie postanowiłem użyć obydwu operatorów. Pytanie, czy poniżej to użycie jest poprawnie wykonane? Postanowiłem wykorzystać jeszcze operator merge, jako że potrzebuję w dwóch miejscach wywołać ręcznie filtrowanie z poziomu kodu (poza samym eventem).

    searchSubject = new Subject<any>();
    searchSubscription: Subscription;

    [...]
    
     ngOnInit() {
        this.initForm();
        this.registerSubscriptions();
        this.searchSubject.next();
    }

    [...]

     registerSubscriptions(): void {
        this.searchSubscription = merge(this.searchForm.valueChanges, this.searchSubject)
            .pipe(debounceTime(500),
                switchMap(() => {
                    this.isBusy = true;
                    return this.service.search(this.prepareCriteria(), false)
                }))
            .subscribe(result => this.assignSearchResult(result));
    }

    [...]

    public sortChanged() {
        this.searchSubject.next();
    }

    [...]

    ngOnDestroy(): void {
        if (this.searchSubject) {
            this.searchSubject.unsubscribe();
        }
        if (this.searchSubscription) {
            this.searchSubscription.unsubscribe();
        }
    }
1
darkrat napisał(a):

Finalnie postanowiłem użyć obydwu operatorów. Pytanie, czy poniżej to użycie jest poprawnie wykonane? Postanowiłem wykorzystać jeszcze operator merge, jako że potrzebuję w dwóch miejscach wywołać ręcznie filtrowanie z poziomu kodu (poza samym eventem).

    searchSubject = new Subject<any>();
    searchSubscription: Subscription;

    [...]
    
     ngOnInit() {
        this.initForm();
        this.registerSubscriptions();
        this.searchSubject.next();
    }

    [...]

     registerSubscriptions(): void {
        this.searchSubscription = merge(this.searchForm.valueChanges, this.searchSubject)
            .pipe(debounceTime(500),
                switchMap(() => {
                    this.isBusy = true;
                    return this.service.search(this.prepareCriteria(), false)
                }))
            .subscribe(result => this.assignSearchResult(result));
    }

    [...]

    public sortChanged() {
        this.searchSubject.next();
    }

    [...]

    ngOnDestroy(): void {
        if (this.searchSubject) {
            this.searchSubject.unsubscribe();
        }
        if (this.searchSubscription) {
            this.searchSubscription.unsubscribe();
        }
    }

Jesli zapytania maja sie wykonac tylko wtedy kiedy this.searchForm.valueChanges i this.searchSubject emituja nowa wartosc to merge jest ok jesli maja sie wykonac gdy ktorykolwiek z nich sie zmieni to uzyj combineLatest

Co do reszty switch map moze byc ewentualnie spojrz na takie operatory jak share, shareReplay itp... https://medium.com/@_achou/rxswift-share-vs-replay-vs-sharereplay-bea99ac42168

0

Dzięki wielkie. Ogólnie w tym wypadku niemożliwe jest, żeby jednocześnie zrobić filtrowanie i sortowanie (filtrowanie odbywa się przez przekazanie forma wewnętrznemu komponentowi, gdzie istnieje subskrypcja na ten event j.w.), a sortowanie odbywa się przez przez uruchomienie metody sortChanged() na każdą zmianę sortowania, w której w środku jest użyty this.searchSubject.next(). Więc skoro za jednym kliknięciem użytkownik nie będzie mógł kliknąć jednocześnie sortowania i filtrowania (ale może to zrobić w bardzo krótkim odstępie czasu), to nie lepiej użyć tutaj combineLatest?

1 użytkowników online, w tym zalogowanych: 0, gości: 1