递归等待订阅集完成
Wait for Subscription set Recursively to Complete
我有一个包含子对象的对象数组,需要递归地在每个对象中设置一个字段(隐藏)。每个值都在订阅中设置。我想等到数组中的每一项都被递归更新后,订阅才完成。
隐藏字段将根据从另一个可观察对象派生的角色和权限进行设置。在示例中,我添加了一个延迟来模拟它。
这是我的第一遍。我确信有一种更简洁的方法来解决这个问题。
https://codesandbox.io/s/rxjs-playground-hp3wr
// Array structure. Note children.
const navigation = [
{
id: "applications",
title: "Applications",
children: [
{
id: "dashboard",
title: "Dashboard"
},
{
id: "clients",
title: "Clients"
},
{
id: "documents",
title: "Documents",
children: [
{
id: "dashboard",
title: "Dashboard"
},...
]
},
{
id: "reports",
title: "Reports"
},
{
id: "resources",
title: "Resources"
}
]
}
];
在代码沙箱示例中,查看控制台消息,我得到了正确的结果。但是,我想避免必须订阅 setHidden 和 recursivelySetHidden。我也想尽可能避免使用 Subject。
这是我的方法:
const roleObservable = timer(1000).pipe(mapTo("**************"));
function populateWithField(o, field, fieldValue) {
if (Array.isArray(o)) {
return from(o).pipe(
concatMap(c => populateWithField(c, field, fieldValue)),
toArray()
);
}
if (o.children) {
return roleObservable.pipe(
tap(role => (fieldValue = role)),
concatMap(role => populateWithField(o.children, field, role)),
map(children => ({
...o,
[field]: fieldValue,
children
}))
);
}
return roleObservable.pipe(
map(role => ({
[field]: role,
...o
}))
);
}
of(navigation)
.pipe(concatMap(o => populateWithField(o, "hidden")))
.subscribe(console.log, e => console.error(e.message));
要注意的主要事情是 concatMap
. It it a higher-order mapping operator 的频繁使用,这意味着,除其他事项外,它将自动从其内部可观察对象订阅 to/unsubscribe。
concatMap
与其他运算符的不同之处在于,它 保留发出值的缓冲区 ,这意味着它将 等待 用于 当前内部可观察对象 在订阅下一个之前完成。
在这种情况下,您将不得不处理很多 Observables-of-Observables(higher-order observables
),这就是 为什么 您必须使用 concatMap
每次遇到一个children
属性。 属性 中的任何 child 都可以有自己的 children
属性,因此您必须确保 Observable 仅包含 first-order 可观测值。
您可以阅读更多关于 higher-order 和 first-order observables here。
我有一个包含子对象的对象数组,需要递归地在每个对象中设置一个字段(隐藏)。每个值都在订阅中设置。我想等到数组中的每一项都被递归更新后,订阅才完成。
隐藏字段将根据从另一个可观察对象派生的角色和权限进行设置。在示例中,我添加了一个延迟来模拟它。
这是我的第一遍。我确信有一种更简洁的方法来解决这个问题。
https://codesandbox.io/s/rxjs-playground-hp3wr
// Array structure. Note children.
const navigation = [
{
id: "applications",
title: "Applications",
children: [
{
id: "dashboard",
title: "Dashboard"
},
{
id: "clients",
title: "Clients"
},
{
id: "documents",
title: "Documents",
children: [
{
id: "dashboard",
title: "Dashboard"
},...
]
},
{
id: "reports",
title: "Reports"
},
{
id: "resources",
title: "Resources"
}
]
}
];
在代码沙箱示例中,查看控制台消息,我得到了正确的结果。但是,我想避免必须订阅 setHidden 和 recursivelySetHidden。我也想尽可能避免使用 Subject。
这是我的方法:
const roleObservable = timer(1000).pipe(mapTo("**************"));
function populateWithField(o, field, fieldValue) {
if (Array.isArray(o)) {
return from(o).pipe(
concatMap(c => populateWithField(c, field, fieldValue)),
toArray()
);
}
if (o.children) {
return roleObservable.pipe(
tap(role => (fieldValue = role)),
concatMap(role => populateWithField(o.children, field, role)),
map(children => ({
...o,
[field]: fieldValue,
children
}))
);
}
return roleObservable.pipe(
map(role => ({
[field]: role,
...o
}))
);
}
of(navigation)
.pipe(concatMap(o => populateWithField(o, "hidden")))
.subscribe(console.log, e => console.error(e.message));
要注意的主要事情是 concatMap
. It it a higher-order mapping operator 的频繁使用,这意味着,除其他事项外,它将自动从其内部可观察对象订阅 to/unsubscribe。
concatMap
与其他运算符的不同之处在于,它 保留发出值的缓冲区 ,这意味着它将 等待 用于 当前内部可观察对象 在订阅下一个之前完成。
在这种情况下,您将不得不处理很多 Observables-of-Observables(higher-order observables
),这就是 为什么 您必须使用 concatMap
每次遇到一个children
属性。 属性 中的任何 child 都可以有自己的 children
属性,因此您必须确保 Observable 仅包含 first-order 可观测值。
您可以阅读更多关于 higher-order 和 first-order observables here。