Отмена асинхронного / ожидающего - PullRequest
3 голосов
/ 05 января 2020

У меня есть функция, подобная приведенной ниже

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

    //do some stuff with Inventories

    // another await 

}

Моя проблема в том, что мне нужно отменить текущий запрос Await и отправить запрос Fre sh Await, как мы делаем с помощью подписки.

Ранее моя функция была такой, как показано ниже, где я мог отменить предыдущую подписку:

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

    })

}

С помощью async / await я мог последовательно обрабатывать любые операции, что действительно здорово, и код был очень хорошо читаем, с помощью которого я не смог добиться использования подписок, но асинхронное / ожидание имело свои недостатки.

Я могу остановить несколько вызовов одновременно, чтобы populateInventories , чтобы избежать моей проблемы, но я ищу, смогу ли я найти еще какие-нибудь решения (аккуратные) для моей проблемы.

PS: я читаю вызов другой подписки в вызове с подпиской или вложенных подписках - не очень хорошая практика.

Ответы [ 2 ]

2 голосов
/ 05 января 2020

Обработка отмены асинхронных действий зависит от type и way , которые мы выполняем asyn c action.

Я предполагаю, что тип наших асинхронных действий - это HTTP-запросы. Для этого у нас есть несколько вариантов выполнения реального HTTP-запроса:

  1. с использованием HttpClient от самого Angular ( самый распространенный случай и лучшая практика! )
  2. с использованием Ax ios (после этого обертка вокруг Fetch API)
  3. с использованием Fetch API , предоставляемым самим браузером

HttpClient

При использовании услуги HttpClient от Angular рекомендуется использовать ее с RxJs в сочетании. Поэтому в вашем случае я бы вернулся к наблюдаемому решению, которое вы описали выше. Почему?:

  • HttpClient сам использует RxJs и возвращает Observables в качестве значения по умолчанию
  • RxJs в сочетании с HttpClient упрощает отмена текущего запроса с его switchMap оператором
  • Имейте в виду, что switchMap сам по себе не отменяет HTTP-запрос или сохраняет для него логи c. Он просто вызывает отписаться на подписку внутреннего потока. Лог отмены запроса c обеспечивается реализацией HttpClient (более или менее), который возвращает лог отмены c в обратном вызове отмены подписки.
  • К сожалению, не так просто написать правильный код RxJs и избежать нескольких или вложенных подписок. В вашем случае я бы попытался создать поток (я полагаю) событий щелчка и использовать:
    • switchMap , чтобы сгладить поток (сравнимый с flatMap) и отписаться от существующего внутреннего поток в случае, если происходит новое событие click.
    • concatMap для ожидания завершения потока inventoriesRequest до создания populateInventoriesRequest.
 this.populateInventoryClicks$
  .pipe(
    switchMap(() => this.inventoryService.inventoriesRequest()),
    concatMap(inventories => this.inventoryService.populateInventoriesRequest(inventories)),
  )
  .subscribe(response => {
    console.log(response);
});

Пожалуйста, взгляните на пример Stackblitz . Отрегулируйте скорость на вкладке сети в консоли разработчика. На вкладке сети мы должны увидеть, что при нескольких нажатиях кнопок выполняется только последний запрос и что все до того, как он был отменен. enter image description here


Ax ios

При использовании клиента Ax ios мы можем использовать метод cancel, который он нам предоставляет. Посмотрите на их документацию , если вы заинтересованы в их подходе. Помните, что когда мы используем Ax ios, по этим запросам не будет работать HttpInterceptors из Angular. Это самая очевидная причина, по которой я не знаю, кто ее использует в сочетании с Angular.


Fetch API

Fetch API также дает нам возможность отменить текущий запрос сделано с fetch. Я не могу рекомендовать этот подход по тем же причинам, по которым я не могу рекомендовать подход Ax ios. Но взгляните , если вам интересно.

0 голосов
/ 05 января 2020

Вы можете попробовать добавить своего рода обратный вызов внутри функции getCustomerInventories, которая разрешает или отклоняет обещание, когда вы хотите, чтобы оно было отменено. Например, если вы хотите, чтобы он останавливался и отправлял новое по времени, вы можете добавить к нему что-то вроде

setTimeout(resolve, ms);

.

Вы можете использовать Promise.race () , чтобы получить либо исходное ожидание, либо ваше время ожидания, либо что бы то ни было. Что-то вроде:

//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
    }




}
...