RxJs switchMap с угловым HttpClient

У меня есть сценарий использования всякий раз, когда запускается новый запрос, любые запросы http, которые уже находятся в полете, должны быть отменены / проигнорированы.

Например:

  • Запрос (скажем, № 2) поступает, в то время как запрос № 1 занимает слишком много времени для ответа / медленного подключения к сети.
  • В этом случае #2 получает очень быстрый ответ от сервера, тогда в любой момент, даже если #1 возвращается с ответом, наблюдаемый ответ HTTP должен игнорироваться.
  • Проблема, с которой я сталкиваюсь, заключается в том, что сначала компонент отображает значения ответов из запроса № 2 и снова обновляется после выполнения требования № 1 (этого не должно происходить).

Я полагал, что switchMap отменяет obervables / поддерживает порядок, в котором испускаются наблюдаемые значения.

выдержка из моего сервиса.

Obervable.of('myServiceUrl')
             .switchMap(url => this.httpRequest(url) )
              .subscribe( response => {
                   // implementation
                   /** Update an observable with the 
                       with latest changes from response. this will be 
                       displayed in a component as async */
                });


private httpRequest(url) {
        return this.httpClient.get('myUrl', { observe: 'response' });
}

Вышеуказанная реализация не работает. Может кто-нибудь выяснить правильную реализацию для этого варианта использования.

3 ответа

Решение

У меня есть фрагмент кода, с которым реализация switchMap была успешной.

class MyClass {
    private domain: string;
    private myServiceUri: subject;
    myData$: Observable<any>;

        constructor(private http: HttpClient) {
            .....
            this.myServiceUri = new Subject();
            this.myServiceUri.switchMap(uri => {
                    return this.http.get(uri , { observe: 'response' })
                            // we have to catch the error else the observable sequence will complete
                            .catch(error => {
                                // handle error scenario
                                return Obervable.empty(); //need this to continue the stream
                            });
                    })
                    .subscribe(response => {
                        this.myData$.next(response);
                    });
        }

        getData(uri: string) {
            this.myServiceUri.next(this.domain + uri); // this will trigger the request     
        }

    }

Похоже, вы создаете несколько наблюдаемых. Из вашего примера непонятно, но кажется, что вы звоните Observable.of каждый раз, когда вы хотите сделать запрос. Это создает новый наблюдаемый поток каждый раз, поэтому для каждого последующего вызова вы получаете новый поток, а предыдущий не отменяется. Вот почему .switchMap не работает.

Если ты хочешь .switchMap чтобы отменить HTTP-запросы, вам нужно, чтобы они использовали один и тот же наблюдаемый поток. Источник Observable, который вы хотите использовать, зависит от того, что именно вызывает HTTP-запросы, но вы можете управлять им самостоятельно, используя что-то вроде Subject,

const makeRequest$ = new Subject();
const myResponse$ = makeRequest$.pipe(switchMap(() => this.service.getStuff()));

Вы можете подписаться на myResponse$ чтобы получить ответ. В любое время, когда вы хотите, чтобы вызвать запрос, вы можете сделать makeRequest$.next(),

Подход, который может показаться похожим на ответ выше, но реализация отличается.

В приведенном ниже примере вы можете видеть, что вызовы API/http отменяются, если вызываются несколько раз. Цель состоит в том, чтобы избежать разрешения вызовов API, что в конечном итоге приводит к медленному ответу браузера. (Пример: Typeahead)

switchMap одновременно может содержать только один наблюдаемый объект. Следовательно, он отменяет остальные вызовы API.

переключатель-map.component.ts

      import { Component, OnInit } from '@angular/core';
import { Subject } from 'rxjs';
import { switchMap } from 'rxjs/operators';
import { HttpClient } from '@angular/common/http';

@Component({
    selector: 'switch-map',
    templateUrl: './switch-map.component.html'
})
export class SwitchMapComponent implements OnInit {
    subject: Subject<string> = new Subject()

    constructor(private http: HttpClient) { }

    makeApiCall(): void {
        this.subject.next('https://jsonplaceholder.typicode.com/photos')
    }

    ngOnInit(): void {
        this.subject.pipe(switchMap(url => this.http.get(url)))
        .subscribe(data => console.log('API Response: ', data))
        setInterval(() => {
            document.getElementById('btn')?.click()
        }, 10)
    }
}

switch-map.component.html

      <button id="btn" (click)="makeApiCall()">Click</button>
Другие вопросы по тегам