【问题标题】:Debounce input to http service call去抖动输入到 http 服务调用
【发布时间】:2017-06-05 07:20:01
【问题描述】:

我目前正在做的是将输入 2-way-binding 设置为 appRequestParams.appName,并且在每个 keyup 事件上,都会调用 fetchApps() 方法。

我正在尝试对输入进行去抖动,这样它就不会立即在每个 keyup 上触发后端 http 请求。

我已经阅读了有关如何进行此操作的方法以及我得出的结论。

HTML

<input #searchBox id="search" mdInput placeholder="Search by list of apps" (keyup)="search(searchBox.value)" />

打字稿

private searchTerms = new Subject<string>();

search(value: string): void {
       this.searchTerms.next(value);
   }


fetchApps() {
       this.appService.query({
           appName: this.appRequestParams.appName ? this.appRequestParams.appName : null,
       }).subscribe(
           (res: ResponseWrapper) => this.onSuccess(res.json, res.headers),
           (res: ResponseWrapper) => this.onError(res.json)
       );
   }


ngOnInit() {
       this.searchTerms
       .debounceTime(300)
       .distinctUntilChanged()
       .switchMap((value: string) => {
           this.appRequestParams.appName = value;
           this.fetchApps();
       });
   }

.switchMap() 行出错:

Argument of type '(value: string) => void' is not assignable to parameter of type '(value: string, index: number) => ObservableInput<{}>'.
  Type 'void' is not assignable to type 'ObservableInput<{}>'.

【问题讨论】:

  • 问题在于返回类型。请检查您的服务的返回类型

标签: html angular typescript


【解决方案1】:

您的代码中与去抖动相关的部分很好。问题是你不能在不返回另一个 observable 的情况下调用 switchMap。

SwitchMap 基本上将 Observable 的最后发出的值转换为另一个 Observable,从而提供了即时自动取消 http 请求的额外能力。

尝试以下方法:

private searchTerms = new Subject<string>();

search(value: string): void {
       this.searchTerms.next(value);
   }


fetchApps() {
       this.appService.query({
           appName: this.appRequestParams.appName ? this.appRequestParams.appName : null,
       });
   }


ngOnInit() {
       this.searchTerms
       .debounceTime(300)
       .distinctUntilChanged()
       .switchMap((value: string) => {
           this.appRequestParams.appName = value;
           return this.fetchApps();
       }).subscribe(
           (res: ResponseWrapper) => this.onSuccess(res.json, res.headers),
           (res: ResponseWrapper) => this.onError(res.json)
       );
   }

一个典型的例子是关联到ActivatedRoute中的路由参数。考虑以下几点:

  @Injectable()
  export class FooService{
    ..
    getFooById(id:string): Observable<FooInterface>{
        return this.http.get('endpoint/${id}')
         .map(res=>res.json())
         .catch(_throw(error));
    }
  }

现在我们导航到类似 root/foo/10980312 的地方,其中最后一部分在路由中定义为 :id

@Component({...})
class FooPreviewComponent{
  data: FooInterface;
  constructor(
    private _route: ActivatedRoute, 
    private _service: FooService) {
     this._route.params
        .switchMap(params=>
          this._service.getFooById(params.id))//switchMap return type is Observable<FooInterface> because of the definition of getFooById
        .subscribe(fooElement => this.data); //intellisense will detect the type of fooElement as FooInterface because of the return type of switchmap
  }
}

在处理 http 期间,如果我们现在导航到 root/foo/1312313,之前的请求将被自动取消。

switchMap 还有其他应用程序,但它需要对内部/外部 observables 及其内部结构有所了解。

您的解决方案效率低下,因为您订阅了去抖动的输入值和由它们触发的 http 响应,但是当您真的只想订阅 http 响应时,您对第一个没有做任何事情。因此,您可以以正确的方式使用 switchMap 来保存其中一个订阅。

【讨论】:

  • 完美。在fetchApps() 方法下需要进行微小的更改。需要return this.appService.query
【解决方案2】:

我最终使用了它,并且效果很好。

   this.searchTerms
   .debounceTime(300)
   .distinctUntilChanged()
   .switchMap(
    (term) => {
        console.log('Term:' + term);
        this.appRequestParams.appName = term;
        this.fetchApps();
        return term;
    })
    .subscribe();
}

【讨论】:

  • 在我看来并不是一个真正的智能解决方案
  • @Jota.Toledo 这是一个快速修复。我想了解switchMapobservables 现在是如何工作的。您的答案产生了相同的错误,因此进行了此修复。也许您可以阐明为什么这不理想?
  • 因此,虽然此解决方案确实解决了 it will not fire off a backend http request on every keyup immediately 问题,但它并不能通过取消先前的“进行中”请求来确保请求响应按顺序返回?
猜你喜欢
  • 2013-03-10
  • 2016-07-17
  • 2019-03-01
  • 2019-12-14
  • 2017-09-28
  • 2020-07-22
  • 1970-01-01
  • 2019-01-21
  • 1970-01-01
相关资源
最近更新 更多