可取消的请求链
Cancellable chain of requests
因此,在我的测试用例研究中,我正在尝试着手 rxjs/observable
,我正在尝试构建一个简单的基于 angular(5) 的网络应用程序,它应该显示有关地理实体的一些统计信息,即国家、州、城市。所以我创建了 changeView
方法,该方法接受三个参数(两个可选)和各自的 ID。
一开始我希望应用程序首先检查我的模型是否已经加载了国家/地区数据,如果没有则查询数据,将检索到的数据保存在模型中,然后检查是否指定了 stateId
和包含在 country.states
数组中然后检查是否已经加载,如果没有查询状态等等,直到检索到所有数据,或者用户更改了输入参数,在这种情况下所有正在进行的请求都应该停止(据我所知,这部分是由 switchMap
操作员完成的),新一轮的请求应该开始了。几天后我明白这太复杂了,所以我改变了计划并决定只在最后将数据存储到模型中。它看起来像我想要的那样,但是子顺序请求没有被取消。以下是我目前所拥有的摘录:
of({countryId: 1, stateId: 1, cityId: 1}).pipe(
debounceTime(500),
switchMap(newState => {
if ((newState !== null))
{
return this.geoStatsService.getCountry(newState.countryId);
}
else
{
return empty();
}
}, (outerValue, innerValue) => {
if ("stateId" in outerValue && outerValue.stateId !== undefined)
{
// ToDo: Check that state is located in this county
return forkJoin(of(innerValue), this.geoStatsService.getStates(outerValue.stateId));
}
else
{
return of(innerValue);
}
}),
mergeAll()
).subscribe(res => {
console.debug("---Subscription---");
console.debug(res);
});
我已经看到,一旦第二个请求被触发,它就不会被取消,数据将到达订阅...在我看来我把它复杂化了,它可能以更优雅的方式完成,这也将按我的预期工作,是这样吗?
附加问题:
我可以,或者更准确的问题 - 我应该从流中提取数据,还是仅在订阅方法中提取数据?
嗯...鉴于 Country/State/City 可能都需要加载,在我看来你需要的是 3 个嵌套的 switchMap,如下所示:
interface Query { countryId: number; stateId?: number; cityId?: number }
// keep a cache to avoid reloading
const countryCache = new Map<number, Country>();
const stateCache = new Map<number, State>();
const cityCache = new Map<number, City>();
const query$: Observable<Query> = of({countryId: 1, stateId: 1, cityId: 1});
query$.pipe(
debounceTime(500),
switchMap((q: Query) => {
if (!q || q.countryId === undefined) {
return of([]);
}
// get country from cache or load from backend
const country$ = countryCache.has(q.countryId) ?
of(countryCache.get(q.countryId)) :
this.geoStatsService.getCountry(q.countryId).do(country => {
countryCache.set(q.countryId, country);
});
return country$.pipe(
switchMap(country => {
if (!country || !country.states.includes(q.stateId)) {
return of([country]);
}
// get state from cache or load from backend
const state$ = stateCache.has(q.stateId) ?
of(stateCache.get(q.stateId)) :
this.geoStatsService.getState(q.stateId).do(state => {
stateCache.set(q.stateId, state);
});
return state$.pipe(
switchMap(state => {
if (!state || !state.cities.includes(q.cityId)) {
return of([country, state]);
}
// get city from cache or load from backend
const city$ = cityCache.has(q.cityId) ?
of(cityCache.get(q.cityId)) :
this.geoStatsService.getCity(q.cityId).do(city => {
cityCache.set(q.cityId, city);
});
return city$.map(city => [country, state, city]);
})
);
})
);
})
).subscribe(([country, state, city]) => {
// ...
});
尝试使用“flatMap”而不是“switchMap”,
switchMap 有自己的取消系统,这就是为什么有时您无法获得订阅。
https://www.learnrxjs.io/operators/transformation/switchmap.html
顺便说一句,你必须订阅,否则代码不会 运行。
因此,在我的测试用例研究中,我正在尝试着手 rxjs/observable
,我正在尝试构建一个简单的基于 angular(5) 的网络应用程序,它应该显示有关地理实体的一些统计信息,即国家、州、城市。所以我创建了 changeView
方法,该方法接受三个参数(两个可选)和各自的 ID。
一开始我希望应用程序首先检查我的模型是否已经加载了国家/地区数据,如果没有则查询数据,将检索到的数据保存在模型中,然后检查是否指定了 stateId
和包含在 country.states
数组中然后检查是否已经加载,如果没有查询状态等等,直到检索到所有数据,或者用户更改了输入参数,在这种情况下所有正在进行的请求都应该停止(据我所知,这部分是由 switchMap
操作员完成的),新一轮的请求应该开始了。几天后我明白这太复杂了,所以我改变了计划并决定只在最后将数据存储到模型中。它看起来像我想要的那样,但是子顺序请求没有被取消。以下是我目前所拥有的摘录:
of({countryId: 1, stateId: 1, cityId: 1}).pipe(
debounceTime(500),
switchMap(newState => {
if ((newState !== null))
{
return this.geoStatsService.getCountry(newState.countryId);
}
else
{
return empty();
}
}, (outerValue, innerValue) => {
if ("stateId" in outerValue && outerValue.stateId !== undefined)
{
// ToDo: Check that state is located in this county
return forkJoin(of(innerValue), this.geoStatsService.getStates(outerValue.stateId));
}
else
{
return of(innerValue);
}
}),
mergeAll()
).subscribe(res => {
console.debug("---Subscription---");
console.debug(res);
});
我已经看到,一旦第二个请求被触发,它就不会被取消,数据将到达订阅...在我看来我把它复杂化了,它可能以更优雅的方式完成,这也将按我的预期工作,是这样吗? 附加问题: 我可以,或者更准确的问题 - 我应该从流中提取数据,还是仅在订阅方法中提取数据?
嗯...鉴于 Country/State/City 可能都需要加载,在我看来你需要的是 3 个嵌套的 switchMap,如下所示:
interface Query { countryId: number; stateId?: number; cityId?: number }
// keep a cache to avoid reloading
const countryCache = new Map<number, Country>();
const stateCache = new Map<number, State>();
const cityCache = new Map<number, City>();
const query$: Observable<Query> = of({countryId: 1, stateId: 1, cityId: 1});
query$.pipe(
debounceTime(500),
switchMap((q: Query) => {
if (!q || q.countryId === undefined) {
return of([]);
}
// get country from cache or load from backend
const country$ = countryCache.has(q.countryId) ?
of(countryCache.get(q.countryId)) :
this.geoStatsService.getCountry(q.countryId).do(country => {
countryCache.set(q.countryId, country);
});
return country$.pipe(
switchMap(country => {
if (!country || !country.states.includes(q.stateId)) {
return of([country]);
}
// get state from cache or load from backend
const state$ = stateCache.has(q.stateId) ?
of(stateCache.get(q.stateId)) :
this.geoStatsService.getState(q.stateId).do(state => {
stateCache.set(q.stateId, state);
});
return state$.pipe(
switchMap(state => {
if (!state || !state.cities.includes(q.cityId)) {
return of([country, state]);
}
// get city from cache or load from backend
const city$ = cityCache.has(q.cityId) ?
of(cityCache.get(q.cityId)) :
this.geoStatsService.getCity(q.cityId).do(city => {
cityCache.set(q.cityId, city);
});
return city$.map(city => [country, state, city]);
})
);
})
);
})
).subscribe(([country, state, city]) => {
// ...
});
尝试使用“flatMap”而不是“switchMap”, switchMap 有自己的取消系统,这就是为什么有时您无法获得订阅。
https://www.learnrxjs.io/operators/transformation/switchmap.html
顺便说一句,你必须订阅,否则代码不会 运行。