You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

RxJS中.reduce(...)返回空数组但.scan(...)正常,原因是什么?

为什么RxJS里reduce替代scan后返回空数组,观察者收不到数据?

嘿,这个问题我之前踩过坑!核心原因其实是reduce和scan这俩操作符的发射时机完全不同,刚好戳中了你当前数据流的特性。

先搞懂两者的核心差异

  • scan是「实时发射中间累计结果」:每次上游Observable发出一个新值,它就会用累加器函数把当前值和之前的累计结果合并,然后立刻把这个更新后的结果发射给观察者。哪怕上游流还在持续发送数据,你的dataSub$观察者每次都能拿到最新的数组。
  • reduce是「等待流完成后发射最终结果」:它会默默收集上游所有发出的值,只有当上游Observable**明确调用complete()**之后,才会把最终的累计结果一次性发射出去。如果你的上游流是一个「不会自动结束」的数据流(比如持续的事件流、一直存活的Subject、WebSocket推送这类),那reduce就会一直等下去,永远不会发射任何结果——你的观察者自然收不到东西,看起来就像返回空数组一样。

结合你的场景分析

你提到用了mergeMap来过滤元素,那大概率你的上游数据流(或者mergeMap内部的流)是不会自动完成的。比如:

  • 如果mergeMap是用来处理一个持续产生数据的源(比如用户输入、实时更新的后端数据),那这个流永远不会触发complete;
  • 或者你没有手动给这个流添加结束的逻辑(比如用take()、takeUntil()这类操作符让流终止)。

这种情况下,scan会每次处理完过滤后的元素就发射累计数组,而reduce一直在等流结束,所以完全没输出。

怎么解决?

  • 如果你的需求就是每次有新元素就更新数组并通知观察者,那scan就是最适合的选择,没必要换成reduce;
  • 如果确实需要等所有元素处理完后再拿到最终数组,那你得确保上游流会触发complete:比如给mergeMap内部的流加take(1)(如果只需要处理一次),或者用takeUntil()监听一个结束信号(比如用户点击「完成」按钮),让reduce能收到结束信号并发射最终结果。

举个简单的代码对比例子:

// 用scan:每次发射中间结果
from([1,2,3]).pipe(
  scan((acc, val) => [...acc, val], [])
).subscribe(arr => console.log(arr));
// 输出:[1], [1,2], [1,2,3]

// 用reduce:只有流完成后才发射最终结果
from([1,2,3]).pipe(
  reduce((acc, val) => [...acc, val], [])
).subscribe(arr => console.log(arr));
// 输出:[1,2,3](只有当from的流完成后才会输出)

内容的提问来源于stack exchange,提问作者Zach LeFevre

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 09:26:15