在我的 Angular 應用程式中,我有一個ProductPageComponent,它顯示了我從服務器獲得的產品。
我還有一個FiltersService存盤一個可觀察的,當我切換過濾器時它會改變值(例如,只顯示可用的產品,只顯示藍色的產品......)。在我的ProductPageComponent中,我訂閱了這個 observable,因此過濾器中的任何更改都會觸發對更新的產品串列的 API 呼叫:
// This is placed inside the component's constructor
this.filtersService.getStaticFiltersObservable()
.pipe(
takeUntil(this.subscriptionSubject$),
distinctUntilChanged((a, b) => JSON.stringify(a) === JSON.stringify(b))
)
.subscribe((filters) => {
productService.getProductsWithFilters(filters)
.then((products) => {
this.productList = products;
});
});
這樣做的問題是,如果由于某種原因可觀察到的過濾器非常快速地更改了兩次值,則對產品的 API 呼叫會執行兩次;如果碰巧第一個電話在第二個電話之后完成(它具有最新版本的過濾器,所以這是我想要的),從第一個電話獲得的產品串列將覆寫從第二個電話獲得的產品串列。
如何防止這種情況并確保我顯示的產品串列始終是最新版本?
uj5u.com熱心網友回復:
如果您試圖忽略的過濾器中的更改非常快(例如,它們之間的毫秒數) ,則可以使用debounceTime運算子:
this.filtersService.getStaticFiltersObservable()
.pipe(
debounceTime(300),
takeUntil(this.subscriptionSubject$)
)
.subscribe((filters) => {
this.productService.getProductsWithFilters(filters)
.then((products) => {
this.productList = products;
});
});
作為評論,在 99% 的情況下,takeUntil(...)運營商應該是最后一個運營商pipe,否則,它不會取消pipe(...)鏈下其他運營商開始的訂閱。在極少數情況下,您希望在pipe(...).
更好的方法
看著你的代碼,在我看來你是 Angular 和整個 observables 世界的新手。更好的方法來處理你正在做的事情(“Angular 方式”)是:
// This goes on the declaration, not in the constructor,
// to keep the constructor code cleaner
productList$ = this.filtersService.getStaticFiltersObservable()
.pipe(
debounceTime(300),
switchMap((filters) => from(this.productService.getProductsWithFilters(filters))
takeUntil(this.subscriptionSubject$)
);
...
constructor(
private productService: ProductService,
private filterService: FilterService
) {}
在您的模板中,您可以使用productList$這種方式:
<ul>
<li *ngFor="let p of productsList$ | async">{{p.name}}</li>
</ul>
如果您可以訪問產品服務并且可以將其回傳值變成可觀察的而不是承諾,那就更好了。這將節省我們from(...)上面使用的運算子來包裝對getProductsWithFilters(...)方法的呼叫。
此外,即使switchMap(...)可以單獨完成這個技巧,我也保留了debouncTime(...)這個用例,因為在我看來,它通過防止早期請求啟動來優化帶寬和服務器資源的使用(無論如何稍后都會被忽略)因為switchMap當它訂閱一個新的 observable 時會中止之前正在進行的訂閱 -是的,我知道這聽起來令人困惑 :D )。
在 Angular 14 中(可能還有更大的版本)
僅供參考,Angular 14 版本帶來了一種注入服務的新方式:inject(...)函式。在撰寫本文時,我想說您可以跟進新聞以了解如何以最佳方式使用它,但我不會立即開始在生產代碼中使用它。它只是 Angular 引入的一種新的 DI 風格,并不打算棄用建構式注入。有些人只是認為該inject(...)功能可以保持代碼整潔并帶來新的可能性。
// Instead of injecting in the constructor, we can
// inject the services by using the inject(...) function
// *only* in the declaration part of a class attribute
productService = inject(ProductService);
filterService = inject(FilterService);
productList$ = this.filtersService.getStaticFiltersObservable()
.pipe(
debounceTime(300),
switchMap((filters) => from(this.productService.getProductsWithFilters(filters))
takeUntil(this.subscriptionSubject$)
);
<ul>
<li *ngFor="let p of productsList$ | async">{{p.name}}</li>
</ul>
uj5u.com熱心網友回復:
正如評論已經表明,當來自過濾器的第二個值比第一個請求完成的速度更快時,您希望使用switchMap運算子取消第一個請求。
它可能看起來像這樣:
this.filtersService.getStaticFiltersObservable()
.pipe(
takeUntil(this.subscriptionSubject$),
distinctUntilChanged((a, b) => JSON.stringify(a) === JSON.stringify(b)),
switchMap((filters) => from(productService.getProductsWithFilters(filters)))
)
.subscribe((products) => {
this.productList = products;
});
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/485627.html
