问题描述
假设您有一个函数返回一个包含对象列表的 rxjs observable。
const getItems = () =>
of([
{
id: 1,value: 10
},{
id: 2,value: 20
},{
id: 3,value: 30
}
]);
const getItem = id =>
of({
id,value: Math.floor(Math.random() * 30) + 1
});
现在我们要创建一个 observable,它将获取第一个列表,并定期随机更新任何列表项。
const source = getItems().pipe(
switchMap(items =>
interval(5000).pipe(
switchMap(x => {
// pick up a random id
const rId = Math.floor(Math.random() * 3) + 1;
return getItem(rId).pipe(
map(item =>
items.reduce(
(acc,cur) =>
cur.id === item.id ? [...acc,item] : [...acc,cur],[]
)
)
);
})
)
)
);
source.subscribe(x => console.log(JSON.stringify(x)));
上述代码的问题在于,每次触发间隔时,上一次迭代中的项目都会重置为其初始形式。例如,
[{"id":1,"value":10},{"id":2,"value":13},{"id":3,"value":30}]
[{"id":1,"value":20},"value":18}]
[{"id":1,"value":16},"value":21},"value":30}]
如您所见,我们的代码在每个时间间隔都会重置列表并更新一个新项目(例如,值 13 在第二次迭代中丢失并恢复为 20)。
这种行为似乎是合理的,因为第一个 items
中的 switchMap
参数就像一个闭包。
我设法通过使用 BehaviorSubject
以某种方式解决了这个问题,但我认为我的解决方案有点脏。
const items$ = new BehaviorSubject([]);
const source = getItems().pipe(
tap(items => items$.next(items)),switchMap(() =>
interval(5000).pipe(
switchMap(() => {
const rId = Math.floor(Math.random() * 3) + 1;
return getItem(rId).pipe(
map(item =>
items$
.getValue()
.reduce(
(acc,cur) =>
cur.id === item.id ? [...acc,[]
)
),tap(items => items$.next(items)),switchMap(() => items$)
);
})
)
)
);
有更好的方法吗?
解决方法
我相信这应该是你想要的:
const source = getItems().pipe(
switchMap(items =>
interval(1000).pipe(
switchMap(() => {
const rId = Math.floor(Math.random() * 3) + 1;
return getItem(rId);
}),scan((acc,item) => {
acc[acc.findIndex(i => i.id === item.id)] = item;
return acc;
},items),)
)
);
这基本上就是你在做什么,但我使用 scan
(用原始 items
初始化)将输出数组保存在 acc
中,以便我稍后更新它再次。