6 votes

Annuler Async/Attente

J'ai une fonction comme ci-dessous

function async populateInventories(custID){
    this.inventories = await this.inventoryService.getCustomerInventories(custID)

    //do some stuff with Inventories

    // another await 

}

Mon problème est que je dois annuler une demande d'attente en cours et envoyer une nouvelle demande d'attente comme nous le faisons avec l'abonnement.

Auparavant, ma fonction était comme ci-dessous où j'étais en mesure d'annuler l'abonnement précédent :

function async populateInventories(custID){

    let invetroySubscription$;

    if(invetroySubscription$!==undefined){
        invetroySubscription$.unsubscribe();
    }

    invetroySubscription$=this.inventoryService.getCustomerInventories(custID).subscribe((data:any)=>{
        this.inventories = data.response;

        //do some stuff with Inventories

         // another subscription here -- I was told this way of doing is wrong

    })

}

Avec async/await, j'ai pu traiter n'importe quelle opération de manière séquentielle, ce qui est vraiment génial, et le code était très lisible, ce qui n'était pas possible avec les abonnements, mais async/await a ses propres inconvénients.

Je peux arrêter plusieurs appels à la fois pour peupler les inventaires pour éviter mon problème, mais je cherche si je peux obtenir d'autres solutions (soignées) à mon problème.

PS : J'ai lu qu'appeler un autre abonnement dans un appel d'abonnement ou des abonnements imbriqués n'est pas une bonne pratique.

4voto

ChrisY Points 911

Le traitement de l'annulation d'une action asynchrone dépend de la méthode de gestion de l'annulation. type et le chemin nous exécutons l'action asynchrone.

Je suppose que le type de nos actions asynchrones sont des requêtes HTTP. Nous avons donc plusieurs options pour exécuter une requête HTTP :

  1. en utilisant HttpClient d'Angular lui-même ( le cas le plus fréquent et la meilleure pratique ! )
  2. en utilisant Axios (afaik un wrapper autour de l'API Fetch)
  3. en utilisant API de récupération fourni par le navigateur lui-même

HttpClient

Lorsque vous utilisez le HttpClient depuis Angular lui-même, il est recommandé de l'utiliser avec le service RxJs en combinaison. Dans votre cas, je reviendrais donc à la solution Observable que vous avez décrite ci-dessus. Pourquoi ?

  • HttpClient lui-même utilise RxJs et renvoie des Observables par défaut
  • RxJs en combinaison avec le HttpClient rend "facile" l'annulation d'une demande en cours avec ses switchMap opérateur
  • Sachez que switchMap n'annule pas la demande HTTP et ne conserve pas la logique de celle-ci. Il appelle simplement se désabonner sur l'abonnement du flux interne. La logique d'annulation de la demande est fournie par l'implémentation de la fonction HttpClient (plus ou moins) qui renvoie la logique d'annulation dans le rappel de désabonnement.
  • Malheureusement, ce n'est pas si facile à écrire correctement. RxJs et éviter les abonnements multiples ou imbriqués. Dans votre cas, j'essaierais de créer un flux d'événements de clics (je suppose) et de l'utiliser :

    • switchMap pour aplanir le flux (comparable à flatMap ) et se désinscrire d'un flux interne existant au cas où un nouvel événement de clics se produit.
    • concatMap d'attendre l'achèvement de la inventoriesRequest avant que le populateInventoriesRequest est faite.

      this.populateInventoryClicks$ .pipe( switchMap(() => this.inventoryService.inventoriesRequest()), concatMap(inventories => this.inventoryService.populateInventoriesRequest(inventories)), ) .subscribe(response => { console.log(response); });

Veuillez jeter un coup d'œil à la Stackblitz exemple. Réduisez votre vitesse dans l'onglet réseau de la console Dev. Dans l'onglet réseau, nous devrions alors voir qu'en cas de clics multiples sur un bouton, seule la dernière requête est complétée et que toutes les précédentes ont été annulées. enter image description here


Axios

Lorsque l'on utilise le client Axios, on peut utiliser la fonction cancel méthode qu'il nous fournit. Jetez un coup d'œil à leur documentation si vous êtes intéressé par leur approche. Sachez que lorsque nous utilisons Axios, aucun outil n'est disponible. HttpIntercepteurs d'Angular s'exécutera sur ces requêtes. C'est la raison la plus évidente pour laquelle je ne connais personne qui l'utilise en combinaison avec Angular.


API de récupération

L'API Fetch nous offre également la possibilité d'interrompre une requête en cours effectuée avec la fonction fetch . Je ne peux pas recommander cette approche pour les mêmes raisons que je ne peux pas recommander l'approche d'Axios. Mais prenez un regardez si vous êtes intéressé.

0voto

SamsTheNerd Points 38

Vous pourriez essayer d'ajouter une sorte de rappel à l'intérieur de la fonction getCustomerInventories qui résout ou rejette la promesse lorsque vous souhaitez qu'elle soit annulée. Par exemple, si vous voulez qu'elle s'arrête et en envoie une nouvelle en fonction du temps, vous pourriez ajouter quelque chose comme

setTimeout(resolve, ms);

à elle.

Vous pourriez utiliser Promise.race() pour récupérer soit l'attente originale, soit votre délai d'attente ou autre. Quelque chose comme :

//keepGoing and checkGoingFunction could be whatever you need them to be but something that returns a promise that will resolve or reject when you want to "cancel" the await
var keepGoing = true;
var checkGoingFunction = function(){
    return new Promise((resolve, reject) => {
        while(keepGoing){
          //just filling time and keeping a loop, maybe not efficient but working
        }
        //after it exits (when you change keepGoing to false)
        resolve("timedOut");
    });
}

function async populateInventories(custID){
    //a promise returned by your method
    var custInvPromise = this.inventoryService.getCustomerInventories(custID)
    var timeoutPromise = checkGoingFunction(); //promise from timeout function
    var bigPromise = Promise.race(custInvPromise, timeoutPromise);
    //maybe add setTimeout(function(){keepGoing = false}, 5000); if you wanted to timeout after 5 seconds

    var result = await bigPromise;
    if(result == "timedOut"){
       //resend await or whatever you need to do
    } else {
        this.inventories = result;
        //do some stuff with Inventories
    }

}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X