说我有一个具有以下签名的函数:
class Item {
String name;
Long id;
}
public Flux<Item> getNew(long id);
getNew()
返回在id(0..N)之后添加的项目流。那么如何将其变成无限流?
是这样的:
public Flux<Item> observe(long id) {
return Flux.interval(Duration.ofSeconds(1)).
flatMap(counter -> getNew(id)); // <-- how to use last value from getNew flux as the new id
}
我能够做到的唯一方法是使用某种类型的状态变量:
public Flux<Long> observe(long id) {
final AtomicLong last = new AtomicLong(id);
return Flux.interval(Duration.ofSeconds(1)).
flatMap(l -> getNew(last.get())).
doOnNext(last::set);
}
是否有更惯用的方法来做到这一点?我试图为此创建生成器,但是我不知道如何实现它。
答案 0 :(得分:1)
如果您可以通过检查来确定Item
发出的最后一个getNew
,则可以使用.expand
运算符:
public Flux<Item> observe(long id) {
return getNew(id)
.expand(item -> isLast(item)
? getNew(item.id)
: Flux.empty());
}
/**
* @return true if the given item is the last item emitted by getNew
*/
private boolean isLast(Item item) {
return // ... snip ...
}
如果您无法通过检查来识别最后一个Item
,则必须使用状态变量。虽然,我建议使用.defer
和.repeat
而不是.interval
...
public Flux<Item> observe(long id) {
final AtomicLong nextStartId = new AtomicLong(id);
return Flux.defer(() -> getNew(nextStartId.get()))
.doOnNext(item -> nextStartId.set(item.id))
.repeat();
}
不使用.interval
的主要原因是:
如果未及时产生需求,则会发出onError信号
因此,如果API花费的时间太长,或者处理结果花费的时间太长,流将以错误结尾。长间隔可能不是问题,但是间隔相对较短(例如您的示例中为1秒),这可能是个问题。
如果要在每次重复迭代之前进行延迟,则可以将.repeatWhen
与带有固定退避值的Reactor-Extra的Repeat
配合使用。这将为您提供“固定延迟”的语义,而不是“固定间隔”。