如何停止对重复函数调用的旧调用



我使用搜索输入从服务器搜索每个字母输入。但我不知道如何阻止旧电话。当我键入包含6个字母的单词时,列表会在秒后连续更新6次。如何在新呼叫中停止旧呼叫?

在html 中输入

<input class="form-control form-control-rounded" placeholder="" [(ngModel)]="searchBody.searchString" (ngModelChange)="searchChanged()">

从输入触发功能:

async searchChanged() {
this.spinner.show('mainSpinner');
await this.getOrders(this.searchBody);
this.spinner.hide('mainSpinner');
}

从searchChanged((触发函数

async getOrders(body) {
let response: any = await this.orderService.getOrders(body);
this.totalOrders = response[0].pagingData.totalCount;
this.orders = response[0].data;
}

订单中的功能服务

async getOrders(body: any) {
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
}
const url = this.baseURL;
let promise = await this.httpClient.post(url , body, httpOptions).toPromise();
let response = await Promise.all([promise]);
return response;
}

有什么想法吗?

您将无法用promise巧妙地做到这一点。这就是Observables派上用场的地方。例如,使用switchMap,当出现新请求时,您可以取消以前的请求;使用debounceTime运算符,您可以控制请求上的特定反跳。

这也需要使用async管道以保持整洁。最好的办法是让你的totalOrdersorders也是可观察的,这样你就不需要在组件内subscribe,因此也可以在销毁时取消订阅。

我看不到这两个变量的模板代码,所以我只向您展示一个如何使用搜索变量来实现这一点的示例:

为了简单起见,删除不必要的东西

<input [ngModel]="searchString$ | async" (ngModelChange)="searchString$.next($event)">
export class SearchComponent implements OnDestroy {
readonly searchString$ = new ReplaySubject<string>(1);
private readonly searchSub: Subscription = this.searchString$.pipe(
tap(() => this.spinner.show('mainSpinner')),
debounceTime(200),
switchMap((search) => this.getOrders(search)),
tap(() => this.spinner.hide('mainSpinner'))
).subscribe()
ngOnDestroy(): void {
this.searchSub.unsubscribe();
}
private getOrders(search: string): Observable<void> {
return this.orderService.getOrders({
...this.searchBody,
searchString: search
}).pipe(
map((response) => {
this.totalOrders = response.pagingData.totalCount;
this.orders = response.data;
})
)
}
}
export class OrderService {
getOrders(body: any): Observable<any> {
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
}
const url = this.baseURL;
return this.httpClient.post(url, body, httpOptions);
}
}

switchMap将确保上一个http请求将被取消。还有一个200的debounceTime,它将确保如果您继续键入,在用户停止键入200ms之前不会请求任何内容。它将大大减少对服务器的(不必要的(请求量。


替代解决方案

subscribe/unsubscribe模式是我不喜欢的,而棱角分明的团队实际上有一个非常漂亮的async管道,可以处理所有这些事情。因此,您甚至可以利用它来简化您的代码。我将给出一些示例代码:

将其视为您的HTML:

<input [ngModel]="searchString$ | async (ngModelChange)="searchString$.next($event)">
<div *ngFor="let order of orders$ | async; trackBy: trackByOrder"></div>
<div>Total orders: {{ totalOrders$ | async}}</div>

你可以这样更新你的组件:

export class SearchComponent {
readonly searchString$ = new ReplaySubject<string>(1);
readonly orderReponse$ = this.searchString$.pipe(
tap(() => this.spinner.show('mainSpinner')),
debounceTime(200),
switchMap((search) => this.orderService.getOrders({
...this.searchBody,
searchString: search
})),
tap(() => this.spinner.hide('mainSpinner')),
shareReplay({ refCount: true, bufferSize: 1 })
);
readonly orders$ = this.orderReponse$.pipe(
map(({ data }) => data)
);
readonly totalOrders$ = this.orderReponse$.pipe(
map(({ pagingData }) => pagingData.totalCount) 
);
constructor(private orderService: OrderService) {}
trackByOrder(idx: number, order: any): string {
// very important for performance sake. Return a unique order id
}
}

注意:如果希望搜索在组件初始化时开始,而不是在用户键入时开始,则应将ReplaySubject<string>(1)更改为BehaviorSubject<string>('')

搜索通常需要一个超时来计算用户在发送实际请求之前按键的时间,这通常在150到200ms以下。

我建议:

  1. 创建全局超时变量
  2. 当搜索输入发生更改时,清除超时变量
  3. 在几秒钟后发送搜索请求

将其转换为代码,您的searchChanged((函数如下所示:

var searchRequestTimeout;
async searchChanged() {
this.spinner.show('mainSpinner');
clearTimeout(this.searchRequestTimeout); 
this.searchRequestTimeout = setTimeout(() => {
await this.getOrders(this.searchBody);
this.spinner.hide('mainSpinner');
}, 150);
}

这将确保在用户仍在键入时永远不会发送请求。

最新更新