Javascript 在遍历树结构时如何避免“shareReplay”
我正在尝试使用RxJS重写包管理器(PM是,PR是) 在重写过程中,我使用了大量的Javascript 在遍历树结构时如何避免“shareReplay”,javascript,rxjs,reactive-programming,Javascript,Rxjs,Reactive Programming,我正在尝试使用RxJS重写包管理器(PM是,PR是) 在重写过程中,我使用了大量的.shareReplay(Infinity),有人告诉我这些都不好(我是反应式编程的初学者) 有人能建议一种替代方法,如何使用.shareReplay(Infinity),重写如下代码: groupBy操作员应该在这里工作。从公共关系来看,这可能过于简单化了,但以下是: 'use strict' const Rx = require('@reactivex/rxjs') const nodes = [ {id
.shareReplay(Infinity)
,有人告诉我这些都不好(我是反应式编程的初学者)
有人能建议一种替代方法,如何使用.shareReplay(Infinity)
,重写如下代码:
groupBy
操作员应该在这里工作。从公共关系来看,这可能过于简单化了,但以下是:
'use strict'
const Rx = require('@reactivex/rxjs')
const nodes = [
{id: 'a', children$: Rx.Observable.empty()},
{id: 'b', children$: Rx.Observable.empty()},
{id: 'c', children$: Rx.Observable.from(['a', 'b', 'd'])},
{id: 'd', children$: Rx.Observable.empty()},
{id: 'e', children$: Rx.Observable.empty()},
]
Rx.Observable.from(nodes)
// Group each of the nodes by its id
.groupBy(node => node.id)
// Flatten out each of the children by only forwarding children with the same id
.flatMap(group$ => group$.single(childId => group$.key === childId))
.subscribe(v => console.log(v));
编辑:比我想象的要难
好的,在我的第二次通读中,我看到这需要比我最初想象的更多的工作,所以它不能如此简单地简化。基本上,你必须在内存复杂度和时间复杂度之间做出选择,因为这里没有灵丹妙药
从优化的角度来看,如果初始源只是一个数组,那么您可以删除shareReplay
,它将以完全相同的方式工作,因为在订阅和ArrayObservable时,唯一的开销是在数组中进行迭代,重新运行源实际上没有任何额外的成本
基本上,我认为你可以考虑两个维度,节点数m
和平均子节点数n
。在速度优化版本中,您将不得不运行m
两次,并且需要迭代“n”个节点。因为你有m*n个孩子,最糟糕的情况是他们都是独一无二的。这意味着您需要执行(m+m*n)
操作,如果我没有弄错的话,这些操作将简化为O(m*n)
。这种方法的缺点是,您需要(nodeId->Node)的映射和删除重复依赖项的映射
'use strict'
const Rx = require('@reactivex/rxjs')
const nodes = [
{id: 'a', children$: Rx.Observable.empty()},
{id: 'b', children$: Rx.Observable.empty()},
{id: 'c', children$: Rx.Observable.from(['a', 'b', 'd'])},
{id: 'd', children$: Rx.Observable.empty()},
{id: 'e', children$: Rx.Observable.empty()},
]
const node$ = Rx.Observable.from(nodes);
// Convert Nodes into a Map for faster lookup later
// Note this will increase your memory pressure.
const nodeMap$ = node$
.reduce((map, node) => {
map[node.id] = node;
return map;
});
node$
// Flatten the children
.flatMap(node => node.children$)
// Emit only distinct children (you can remove this to relieve memory pressure
// But you will still need to perform de-duping at some point.
.distinct()
// For each child find the associated node
.withLatestFrom(nodeMap$, (childId, nodeMap) => nodeMap[childId])
// Remove empty nodes, this could also be a throw if that is an error
.filter(node => !!node)
.subscribe(v => console.log(v));
另一种方法是使用与您类似的方法,该方法侧重于以性能为代价降低内存压力。请注意,就像我说的,如果源代码是数组,则基本上可以删除shareReplay,因为它在重新计算时所做的只是重新迭代数组。这将删除附加映射的开销。虽然我认为您仍然需要distinct来删除重复项。最糟糕的运行时复杂性是O(m^2*n)
,或者如果n
很小,则只需O(m^2)
,因为您需要遍历所有子节点,并且对于每个子节点,您还需要再次遍历m
,以找到匹配的节点
const node$ = Rx.Observable.from(nodes);
node$
// Flatten the children
.flatMap(node => node.children$)
// You may still need a distinct to do the de-duping
.flatMap(childId => node$.single(n => n.id === childId)));
我想说,在几乎所有的情况下,第一个选项都更可取,但我将其留给您根据您的用例来确定。可能是您建立了某种启发式,在某些情况下选择一种算法而不是另一种算法
旁注:对不起,这不是那么容易,但爱pnpm,所以继续做好工作 谢谢,我今晚就试试这个。您认为这会提高性能吗?(或RAM使用情况)@ZoltanKochan修复了问题,最终版本的性能不如我最初想象的那么好,但我认为,如果您要优化性能,最好牺牲一点RAM来优化分辨率行为。速度更重要。看起来第一部分基本上就是我需要的,但是
reduce
的用法不太好reduce
将等待传递所有节点。我需要它尽快通过。尽管某些DEP可能无法解析pnpm
但可以开始积极链接已解析的DEP。无论如何,它是有用的,我学到了很多,并将重新分析我的PR。我不确定我是否能找到解决方法,为了解决任何依赖关系,您需要有一个可用类型的完整列表。虽然我认为您可以进行某种延迟解析,在这里您可以预先解析您可以解析的内容并通过反馈返回未解析的值,但是这会有所帮助吗,因为最终您仍然是CPU受限的?
const node$ = Rx.Observable.from(nodes);
node$
// Flatten the children
.flatMap(node => node.children$)
// You may still need a distinct to do the de-duping
.flatMap(childId => node$.single(n => n.id === childId)));