简体   繁体   English

如何在 Angular 6 中取消 http 请求?

[英]How to cancel http request in Angular 6?

I have a page with three components: 1. Products list component which gets some products as input and display them.我有一个包含三个组件的页面: 1. 产品列表组件,它获取一些产品作为输入并显示它们。 2. Filters component which displays some filters list ie (size, colour,...) and also display the added filters. 2. Filters 组件,显示一些过滤器列表,即(大小,颜色,...)并显示添加的过滤器。 3. Main component which is the root component 3. Main组件是根组件

Let say a user adds 1 filter which fires a http request to get new filtered products and while the request is pending he removes the added filter which fires another http request to fetch all the products How to cancel the first request so we don't display the filtered products?假设用户添加了 1 个过滤器,该过滤器会触发一个 http 请求以获取新的过滤产品,并且在请求待处理时他删除了添加的过滤器,该过滤器会触发另一个 http 请求以获取所有产品如何取消第一个请求以便我们不显示过滤后的产品? Here is my code:这是我的代码:

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() {
  }

}

Long story short:长话短说:

Easiest way to handle such situations is by using the switchMap operator.处理这种情况的最简单方法是使用 switchMap 运算符。 What this does is cancel the internal subscription as soon as a new event comes along.这样做是在新事件出现时立即取消内部订阅。

One implementation would be:一种实现是:

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);
  }
}

Long story:很长的故事:

What happens here is: When you change filter the onFilterChange is triggered.这里发生的是:当您更改过滤器时,将触发 onFilterChange。 You then emit the latest filters (inside this.filtersService.filters) through the _filters$ Subject (a subject is almost identical to an EventEmitter).然后,您通过 _filters$ 主题(主题几乎与 EventEmitter 相同)发出最新的过滤器(在 this.filtersService.filters 内)。

Back in time during component initialization the ngOnInit method has called setProducts, which has subscribed to the _filters$ subject for future events (none has happened at this point).回到组件初始化期间,ngOnInit 方法调用了 setProducts,它已经为未来事件订阅了 _filters$ 主题(此时没有发生)。 When an event arrives on _filters$ then we trigger the getProducts method of dataservice, passing it the filters that where contained in the event.当事件到达 _filters$ 时,我们会触发数据服务的 getProducts 方法,将事件中包含的过滤器传递给它。 We will be waiting on this line until the http call has completed.我们将在此行等待,直到 http 调用完成。 As soon as it completes the result of the http call will be assigned to the products of the component.一旦完成,http 调用的结果将被分配给组件的产品。

If while we are waiting for the http response to get back, onFiltersChange is fired again, then a new event will arive at the switchMap and it will cancel the previous http request so that it can handle the new event.如果在我们等待 http 响应返回时,onFiltersChange 再次被触发,那么一个新事件将到达 switchMap 并且它将取消之前的 http 请求,以便它可以处理新事件。

This is a very powerful approach as changing a single operator, you can easily change the behavior of your app.这是一种非常强大的方法,因为更改单个运算符,您可以轻松更改应用程序的行为。 For instance, changing switchMap to concatMap will make the request wait for the previous one to complete (will happen serially).例如,将 switchMap 更改为 concatMap 将使请求等待前一个请求完成(将连续发生)。 Changing it to flatMap will have the same behaviour as the original code you posted (http requests will happen as soon as filters change, without affecting previous ones, order of responses will not predictable) and so on.将其更改为 flatMap 将具有与您发布的原始代码相同的行为(http 请求将在过滤器更改后立即发生,而不会影响以前的请求,响应顺序将不可预测)等等。

Note: to cancel the request just use unsubscribe.注意:取消请求只需使用取消订阅。

For exmple例如

   const course$ = this.service$.getCourses(`/api/courses`).subscribe(courses => { console.log(courses) }

   setTimeout(() => course$.unsubscribe(),1000) // cancel the request

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM