Mutiny中Vert.x共享计数器返回Uni为何需使用transformToUni?
核心原理说明
你产生困惑的核心原因是搞错了最上游方法的返回类型:
vertx.sharedData().getCounter(COUNTER_NAME)本身不会直接返回Counter实例,它返回的是Uni<Counter>——因为获取共享计数器引用本身就是异步非阻塞操作,Vert.x不会阻塞当前线程等结果,只会先返回一个响应式包装对象,等异步操作完成后才会在内部吐出Counter实例。
你没有办法脱离响应式操作符直接拿到这个Uni内部包裹的Counter对象,自然也没法直接调用counter.get()。
而counter.get()本身确实返回Uni<Long>,这时候如果你用普通的transform操作符来做映射,写法会是:
// 错误写法,最终返回类型是Uni<Uni<Long>>,不符合方法要求 return this.vertx.sharedData().getCounter(COUNTER_NAME) .onItem().transform(counter -> counter.get());
这种写法会产生嵌套的响应式类型,也就是你拿到的是外层包了一层的Uni,它内部的item才是真正取计数值的UnitransformToUni的作用就是做「扁平化映射」:它接收一个函数,这个函数用上一步吐出的item(也就是Counter实例)返回一个新的Uni,操作符会自动订阅这个内部的Uni,把它吐出的结果直接作为整个流的最终输出,刚好把两层Uni拍平成一层,最终得到你需要的Uni<Long>类型,这也是你这段代码能正常运行的根本原因。
可读性更好的等价写法
你现在写的写法本身是完全符合Mutiny规范的正确实现,没有逻辑问题。如果觉得transformToUni的命名不够直观,在当前稳定版Mutiny中可以直接用语义更明确的别名flatMap,两者行为完全一致,写出来的代码逻辑更顺:
public Uni<Long> getCounterValue() { return this.vertx.sharedData() .getCounter(COUNTER_NAME) .flatMap(counter -> counter.get()); }
注意:绝对不要为了省事儿用阻塞方式提前拿值,比如写
counter.get().await().indefinitely()这种代码,会直接阻塞Vert.x事件循环线程,违反响应式编程和Vert.x的核心设计原则,会引发性能问题甚至线程死锁。
内容的提问来源于stack exchange,提问作者gkatz
相关产品推荐
相关产品推荐

