内容提要
本文介绍三种反应式编程算法:推、拉、推拉混合,用于处理数据变化时的高效更新。推模式精准但易乱序和泄漏中间状态;拉模式简单安全但全量重算浪费;推拉结合先标记脏格子再计算,兼顾效率与正确性。选择取决于数据规模、改动频率和中间状态容忍度,无完美算法,需按场景取舍。
延伸解读
推模式的乱序与中间状态问题
推模式虽然精准,但存在两个隐患:一是依赖关系复杂时,一个格子可能被多次通知,导致重复计算;二是更新过程中,其他代码可能读到中间状态,造成数据不一致。文章以A、B、C、D格子为例,说明D可能收到三次通知,前两次浪费。解决乱序需要全局排序,但这与推模式“只知邻居”的设计相悖。中间状态泄漏则需靠自觉或额外机制规避。
拉模式的缓存失效难题
拉模式通过函数调用自动处理依赖,天然避免中间状态,但全量重算代价高。引入缓存可缓解,但缓存失效是难点:改一个输入,哪些缓存该清?清错会导致数据错误,漏清则返回旧值。文章指出,拉模式无法预知哪些格子会变,只能全算,因此数据量大时效率低下。React的组件树更新是折中方案,但非通用。
推拉结合的两阶段设计
推拉结合分两步:先推标记脏格子,再拉计算。标记阶段无副作用,每个格子只插一次旗,顺序无关;计算阶段递归处理脏依赖,确保无中间状态。此模式兼顾效率与正确性,但要求整个拉过程在一个时间片内完成,若计算过重需拆成状态机,增加复杂度。
选型建议:按场景取舍
文章强调无完美算法,选择取决于数据规模、改动频率和中间状态容忍度。推模式适合事件驱动、依赖链浅的场景;拉模式适合依赖复杂但数据量小;推拉结合适合大型表格或复杂GUI。实际开发前,应评估数据量、改动频率和中间状态影响,再决定采用哪种模式。
Q&A
推模式、拉模式和推拉混合模式分别是什么?
推模式是数据变化时主动通知所有依赖它的格子重新计算;拉模式是输出格子需要时向上层询问,直到输入格子,再逐层返回;推拉混合模式先通过推标记所有受影响的格子为脏,再通过拉只计算这些脏格子。
推模式有什么优缺点?
推模式优点是精准,只通知依赖的格子,适合事件驱动场景。缺点是可能重复计算(如B依赖A和C,C又依赖A,B会收到两次通知),且容易泄漏中间状态(更新过程中其他代码可能读到不一致的值)。
拉模式为什么不会出现中间状态问题?
因为拉模式的所有计算都在一次递归调用中完成,中间不会插入其他操作,所以不会出现中间状态被外部看到的问题。
推拉混合模式是如何工作的?
分两步:第一步是推,从修改的输入格子开始,向下遍历,给每个受影响的格子插上脏标记,并记录输出格子;第二步是拉,处理待更新清单中的输出格子,向上询问依赖,若依赖是脏的则递归计算并擦除标记,最终所有格子更新为最新值。
推拉混合模式相比推模式和拉模式有什么优势?
它结合了两者优点:效率高(每个脏格子只访问一次),只计算受影响的格子,无中间状态(计算集中在拉阶段),支持动态依赖,且不需要全局顺序。
在实际开发中如何选择推、拉或推拉混合模式?
根据数据规模、改动频率和中间状态容忍度选择:推模式适合事件驱动且影响范围小的场景;拉模式适合依赖复杂但数据量不大的场景(如React组件树);推拉混合适合大型电子表格或复杂GUI,但需保证更新能在单次循环完成。