【问题标题】:Cancel Async/Await取消异步/等待
【发布时间】:2020-04-23 08:13:09
【问题描述】:

我有一个类似下面的函数

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

    //do some stuff with Inventories

    // another 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 我能够按顺序处理任何操作,这真的很棒,而且代码可读性非常好,我无法使用订阅来实现,但是 async/await 有它们自己的缺点。

我可以一次停止多次调用 populateInventories 以避免我的问题,但我正在寻找是否可以为我的问题找到更多解决方案(整洁)。

PS:我读到在订阅调用或嵌套订阅中调用另一个订阅不是一个好习惯。

【问题讨论】:

  • 看起来.getCustomerInventories(custID) 没有返回承诺。它(同步地)返回一个可以订阅的类流对象。因此,等待getCustomerInventories 不会改进您的原始代码。事实上,等待它并将结果视为您的数据(而不是获取您的数据的流)几乎肯定是一个错误。
  • 从你试图实现的目标来看,你的异步逻辑是错误的。你不去等待一个新的承诺,你改变承诺逻辑以满足额外/新的请求,即你更新inventoryService.getCustomerInventories实现,这样它就可以在内部刷新请求,链接到它返回的承诺。

标签: javascript async-await angular6 es6-promise


【解决方案1】:

处理异步操作的取消取决于我们执行异步操作的类型和方式。

我假设我们异步操作的类型是 HTTP 请求。因此,我们有多种选择如何执行实际的 HTTP 请求:

  1. 使用来自 Angular 本身的 HttpClient(最常见的情况和最佳实践!)
  2. 使用 Axios(Fetch API 的封装)
  3. 使用浏览器本身提供的Fetch API

HttpClient

当使用 Angular 本身的 HttpClient 服务时,建议将其与 RxJs 组合使用。所以在你的情况下,我会切换回你上面描述的 Observable 解决方案。为什么?:

  • HttpClient 本身使用RxJs 并默认返回 Observables
  • RxJs 与 HttpClient 结合使用可以“轻松”通过其 switchMap 运算符
  • 取消正在进行的请求
  • 请注意,switchMap 本身不会取消 HTTP 请求或保留其逻辑。它只是在订阅内部流时调用unsubscribe。请求取消逻辑由 HttpClient 的实现提供(或多或少),它在取消订阅回调中返回取消逻辑。
  • 不幸的是,编写正确的RxJs 代码并避免多个或嵌套订阅并不容易。在你的情况下,我会尝试创建一个(我假设)点击事件流并使用:
    • switchMap 扁平化流(与flatMap 相比)并取消订阅现有的内部流,以防出现新的点击事件 发生。
    • concatMap 等到 inventoriesRequest 流完成,然后再创建 populateInventoriesRequest。
 this.populateInventoryClicks$
  .pipe(
    switchMap(() => this.inventoryService.inventoriesRequest()),
    concatMap(inventories => this.inventoryService.populateInventoriesRequest(inventories)),
  )
  .subscribe(response => {
    console.log(response);
});

请查看 Stackblitz 示例。在开发控制台的网络选项卡中限制您的速度。 然后,在网络选项卡中,我们应该看到在多次单击按钮时,只有最后一个请求完成,而之前的所有请求都被取消了。


Axios

在使用Axios客户端时,我们可以使用它为我们提供的cancel方法。如果您对他们的方法感兴趣,请查看他们的documentation。 请注意,当我们使用 Axios 时,Angular 中的 HttpInterceptors 不会在这些请求上运行。这是我知道没有人将它与 Angular 结合使用的最明显原因。


获取 API

Fetch API 还为我们提供了中止使用fetch 发出的正在进行的请求的可能性。我不能推荐这种方法,原因与我不能推荐 Axios 方法的原因相同。但是,如果您有兴趣,请发送look。

【讨论】:

  • 这是一个很好的答案,但我不明白它与问题有何关系。我认为你的观点是我们不会取消承诺,我们会取消返回承诺的异步操作。 OP 似乎在处理流,而不是承诺。
  • HttpClient 部分处理流。关键是在 Angular 中,将HttpClient 与 RxJs 结合使用会更容易,尤其是在取消用例中,因此没有任何承诺。
【解决方案2】:

您可以尝试在 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
    }




}

【讨论】:

  • 几乎可以肯定 getCustomerInventories(custID) 没有返回承诺。
  • @danh 啊,我对订阅不太了解,我的错 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-11-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-12
  • 1970-01-01
  • 2022-07-06
相关资源
最近更新 更多