在switchMap中操作时保持状态



假设你有一个函数返回一个rxjs可观察对象,其中包含一个对象列表。

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
});

现在我们想要创建一个可观察对象,它将获得第一个列表,并在一定的时间间隔内随机更新列表中的任何项。

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":10},{"id":2,"value":20},{"id":3,"value":18}]
[{"id":1,"value":10},{"id":2,"value":16},{"id":3,"value":30}]
[{"id":1,"value":21},{"id":2,"value":20},{"id":3,"value":30}]

如您所见,在每个间隔中,我们的代码都在重置列表并更新一个新项(例如值13在第二次迭代中丢失并恢复为20)。这种行为似乎是合理的,因为第一个switchMap中的items参数就像闭包一样。

我设法通过使用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, item] : [...acc, cur],
[]
)
),
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,所以我可以更新它以后再次。

实时演示:https://stackblitz.com/edit/rxjs-kvygy1?file=index.ts

最新更新