设置如下。有人用包含id的url来调用我的angular应用,以显示带有该id的对象的属性。就像网上商店一样。
http://myFrontend/productDetails/10
然后使用ActivatedRoute
获取id (10
)并将其传递到HttpClient
以从后端获取所请求的数据。
this.route.params.pipe(map(params => params.id)).subscribe(id => {
const params = new HttpParams().set('id', id);
this.httpClient.get<Product>('http://myBackend/product/', {params})
.subscribe(product => this.product = product);
});
当用户从这条路线导航到不同产品的相同路线时,问题就会显现出来。假设你在http://myFrontend/productDetails/10
。有一个Observable
等待后端返回id为10
的产品,并将其存储到一个组件变量中。然后单击next
以导航到http://myFrontend/productDetails/11
。创建一个新的Observable
,等待id为11
的产品将其存储到相同的组件变量中。
我真的不明白为什么,但是当我想获取产品11
时,返回产品10
的第一个Observable
再次触发。包括对http://myBackend/product/?id=10
的重复http请求。这反过来又会导致竞争条件,因为两个Subscriptions
都将其结果写入相同的组件变量。
我可以通过在可观察对象第一次返回后立即取消订阅或通过take(1)
管道来阻止这种情况的发生,但我想了解为什么会发生这种情况。
this.route.params
在每次参数改变时触发。这意味着每次参数改变时,它都会创建一个新的订阅。正如您在示例中所看到的,嵌套订阅通常是一种反模式。如果你想链接可观察对象,你可以使用所谓的高阶映射操作符,例如concatMap、mergeMap、switchMap或排气图。您可以做的另一件事是不通过调用subscribe显式订阅,而是可以直接在模板中利用async管道。这将自动取消订阅,这可以防止内存泄漏(这在您的情况下不是问题)。试试这个:
this.route.params.pipe(concatMap(params => {
const p = new HttpParams().set('id', params.id);
return this.httpClient.get<Product>('http://myBackend/product/', {p})
}).subscribe(product => this.product = product);
或
let product$ = this.route.params.pipe(concatMap(params => {
const p = new HttpParams().set('id', params.id);
return this.httpClient.get<Product>('http://myBackend/product/', {p})
})
...in the template
<div *ngIf="product$ | async as product">....</div>