【问题标题】:How to cancel http request in Angular 6?如何在 Angular 6 中取消 http 请求?
【发布时间】:2020-03-15 01:16:02
【问题描述】:

我有一个包含三个组件的页面: 1.产品列表组件,它获取一些产品作为输入并显示它们。 2.过滤器组件显示一些过滤器列表,即(大小,颜色,...),并显示添加的过滤器。 3. 主组件,即根组件

假设用户添加了 1 个过滤器,该过滤器触发 http 请求以获取新的过滤产品,并且在请求待处理时,他删除了添加的过滤器,该过滤器触发另一个 http 请求以获取所有产品 如何取消第一个请求,以便我们不显示过滤后的产品? 这是我的代码:

class FiltersService {
  private _filters: any[];

  get filters() {
    return this._filters;
  }
  addFilter(filter) {
    this._filters.push(filter);
  }

  removeFilter(filter) {
    // Remove filter logic ...
  }

}

class DataService_ {
  constructor(private http: HttpClient) {

  }

  getProducts(filters) {
    return this.http.post<any[]>('api/get-products', filters)
  }

}


@Component({
  selector: 'app-main',
  template: `
  <div>
      <app-filters [filtersChanged]="onFiltersChange()"></app-filters>
      <app-products-list [products]="products"> </app-products-list>
</div>
`
})
class MainComponent {
  products: any[];

  constructor(private dataService: DataService_, private filtersService: FiltersService) {

  }

  ngOnInit() {
    this.setProducts()
  }

  setProducts() {
    let filters = this.filtersService.filters;
    this.dataService.getProducts(filters)
      .subscribe(products => this.products = products)
  }

  onFiltersChange() {
    this.setProducts();
  }
}
@Component({
  selector: 'app-filters',
  template: `
  <div>
 Filters : 
 <ul>
     <li *ngFor="let filter of filters" (click)="addFilter(filter)"> {{ filter.name }}</li>
 </ul>

 <hr>
 Added Filters:
 <ul>
 <li *ngFor="let filter of filtersService.filters"> {{ filter.name }}  <button (click)="removeFilter(filter)"> Remove</button></li>
</ul>
</div>

`
})
class FiltersComponent {
  filters = [{ name: 'L', tag: 'size' }, { name: 'M', tag: 'size' }, { name: 'White', tag: 'colour' }, { name: 'Black', tag: 'colour' }]
  @Output() filtersChanged = new EventEmitter()
  constructor(public filtersService: FiltersService) {

  }

  addFilter(filter) {
    const isAdded = this.filtersService.filters.find(x => x.name === filter.name);
    if (isAdded) return;
    this.filtersService.addFilter(filter);
    this.filtersChanged.emit()
  }

  removeFilter(filter) {
    this.filtersService.remove(filter);
    this.filtersChanged.emit()
  }

}

@Component({
  selector: 'app-products-list',
  template: `
  <div>
  <h1>Products</h1>
  <ul *ngIf="products.length">
      <li *ngFor="let product of products">
          {{product.name }}
      </li>
  </ul>
</div>
`
})
class ProductsListComponent {
  @Input() products
  constructor() {
  }

}

【问题讨论】:

    标签: javascript angular rxjs


    【解决方案1】:

    长话短说:

    处理这种情况的最简单方法是使用 switchMap 运算符。这样做的目的是在有新事件出现时立即取消内部订阅。

    一种实现方式是:

    class MainComponent {
      products: any[];
      private _filters$ = new Subject();
    
      constructor(private dataService: DataService_, private filtersService: FiltersService) {
    
      }
    
      ngOnInit() {
        this.setProducts()
      }
    
      setProducts() {
        this._filters$
            .switchMap((filters)=> this.dataService.getProducts(filters)) // or .let(switchMap...) if you are using rxjs >5.5
            .subscribe(products => this.products = products);
      }
    
      onFiltersChange() {
        this._filters$.next(this.filtersService.filters);
      }
    }
    

    长篇大论:

    这里发生的是: 当您更改过滤器时,会触发 onFilterChange。然后,您通过 _filters$ 主题(主题几乎与 EventEmitter 相同)发出最新的过滤器(在 this.filtersService.filters 中)。

    在组件初始化期间,ngOnInit 方法调用了 setProducts,它订阅了 _filters$ 主题以备将来事件(此时没有发生)。当事件到达 _filters$ 时,我们触发 dataservice 的 getProducts 方法,将事件中包含的过滤器传递给它。我们将在这条线上等待,直到 http 调用完成。一旦完成,http 调用的结果将分配给组件的产品。

    如果在我们等待 http 响应返回时,再次触发 onFiltersChange,则新事件将到达 switchMap 并取消之前的 http 请求,以便它可以处理新事件。

    这是一种非常强大的方法,因为更改单个运算符,您可以轻松更改应用程序的行为。例如,将 switchMap 更改为 concatMap 将使请求等待前一个完成(将连续发生)。将其更改为 flatMap 将具有与您发布的原始代码相同的行为(http 请求将在过滤器更改后立即发生,而不影响之前的请求,响应顺序将无法预测)等等。

    【讨论】:

    • 谢谢,帮了大忙:)
    • 不客气。您还应该接受这个答案(如果您认为它是正确的),以帮助未来的读者理解它可以立即解决您的问题。
    【解决方案2】:

    注意:要取消请求,只需使用 unsubscribe。

    例如

       const course$ = this.service$.getCourses(`/api/courses`).subscribe(courses => { console.log(courses) }
    
       setTimeout(() => course$.unsubscribe(),1000) // cancel the request
    

    【讨论】:

      猜你喜欢
      • 2019-01-08
      • 1970-01-01
      • 1970-01-01
      • 2018-12-12
      • 1970-01-01
      • 1970-01-01
      • 2019-11-17
      • 2018-10-16
      • 1970-01-01
      相关资源
      最近更新 更多