web-dev-qa-db-fra.com

Angular2: switchMap n'annule pas les appels http précédents

J'essaie d'utiliser switchMap pour annuler tous les appels http précédents dans Angular2 . Le code est fondamentalement

var run = ():Observable<any> => {
        var url = 'http://...'
        return this._http.get(url)
            .map(result => {
                return var xmlData:string = result.text()

            });
    }


    function pollTasks() {
        return Observable.of(1)
            .switchMap(() => run())
            .map(res => res)
    }

    // caller can do subscription and store it as a handle:
    let tasksSubscription =
        pollTasks()
            .subscribe(data => {
                console.log('aa'+data)
            });

et ainsi j’appelle la source entière plusieurs fois de suite et reçois plusieurs réponses (exemple: aa + data)

J'étais sous l'impression switchMap devrait annuler les appels précédents.

14
born2net

Les demandes doivent provenir du même flux sous-jacent. Voici une fonction d'usine qui créera une instance de service http qui devrait faire ce que vous voulez:

function httpService(url) {
   // httpRequest$ stream that allows us to Push new requests
   const httpRequest$ = new Rx.Subject();

   // httpResponse$ stream that allows clients of the httpService
   // to handle our responses (fetch here can be replaced with whatever
   // http library you're using).
   const httpResponse$ = httpRequest$
       .switchMap(() => fetch(url));


   // Expose a single method get() that pushes a new
   // request onto the httpRequest stream. Expose the
   // httpResponse$ stream to handle responses.
   return {
       get: () => httpRequest$.next(),
       httpResponse$
   };
}

Et maintenant, le code client peut utiliser ce service comme ceci:

const http = httpService('http://my.api.com/resource');

// Subscribe to any responses
http.httpResponse$.subscribe(resp => console.log(resp));

// Call http.get() a bunch of times: should only get one log!!
http.get();
http.get();
http.get();
21
Calvin Belden
constructor(private _http:Http, private appStore:AppStore) {

        this.httpRequest$ = new Subject();


        this.httpRequest$
            .map(v=> {
                return v;
        })
        .switchMap((v:any):any => {
            console.log(v);
            if (v.id==-1||v.id=='-1')
                return 'bye, cancel all pending network calls';                
            return this._http.get('example.com)
                .map(result => {
                    var xmlData:string = result.text()
                });
        }).share()
        .subscribe(e => {                
        })
    ...

et pousser des données dans:

this.httpRequest$.next({id: busId});        

cela fonctionne très bien et je peux maintenant avoir un seul service par lequel je peux diriger tous les appels réseau, ainsi qu'annuler les appels précédents ...

voir l'image ci-dessous, au fur et à mesure que de nouveaux appels arrivent, les précédents sont annulés.

 enter image description here

7
born2net

Je pense que lorsque vous utilisez l'opérateur switchMap, vous ne pouvez annuler que les demandes du flux de données actuel. Je veux dire les événements qui se produisent sur la même chaîne observable ...

Si vous appelez votre méthode pollTasks plusieurs fois, vous ne pourrez pas annuler les demandes précédentes car elles ne figureront pas dans le même flux de données ... Vous créez une chaîne observable chaque fois que vous appelez la méthode.

Je ne sais pas comment vous déclenchez l'exécution de vos demandes.

Si vous voulez exécuter votre requête toutes les 500 ms, vous pouvez essayer ceci:

pollTasks() {
  return Observable.interval(500)
                .switchMap(() => run())
                .map(res => res.json());
}

Dans ce cas, s'il y a une demande en cours après 500 ms, elles seront annulées pour exécuter la nouvelle.

Avec l'approche, il vous suffit d'appeler une fois la méthode pollTasks.

Vous pouvez également déclencher la chaîne de traitement asynchrone en fonction d'événements utilisateur. Par exemple, lorsque les caractères sont renseignés dans les entrées:

var control = new Control();
// The control is attached to an input using the ngFormControl directive
control.valueChanges.switchMap(() => run())
                .map(res => res.json())
                .subscribe(...);

Il existe une proposition pour lier/initier plus facilement la chaîne de traitement sur les événements DOM (fromEvent)

Voir ce lien:

3
Thierry Templier

Vous pouvez utiliser Objet, mais si vous le faites, vous devez gérer la souscription et la publication. Si vous souhaitez simplement une méthode renvoyant Observable qui annule les demandes dans un intervalle, voici comment procéder: 

observer: Observer<CustomObject>;
debug = 0;

myFunction(someValue) {
    this.observable = new Observable<CustomObject>(observer => {
        if (!this.observer) {
            this.observer = observer;
        }

       // we simulate http response time
        setTimeout(() => {
            this.observer.next(someValue);
            this.debug++;
        }, 1000);
    })
    .debounceTime(3000) // We cancel request withing 3s interval and take only the last one
    .switchMap(fn)
    .do(() => {
        console.log("debug", this.debug);
    });
}

En utilisant toujours le même observateur, annulons les demandes selon tout ce que nous voulons (debounceTime, distinctUntilChanged, ...). 

0
marvinroller