我正在尝试反应堆库,而且我无法弄清楚为什么下面的单声道永远不会返回onNext或onComplete调用。我想我错过了非常微不足道的事情。这是一个示例代码。
MyServiceService service = new MyServiceService();
service.save("id")
.map(myUserMono -> new MyUser(myUserMono.getName().toUpperCase(), myUserMono.getId().toUpperCase()))
.subscribe(new Subscriber<MyUser>() {
@Override
public void onSubscribe(Subscription s) {
System.out.println("Subscribed!" + Thread.currentThread().getName());
}
@Override
public void onNext(MyUser myUser) {
System.out.println("OnNext on thread " + Thread.currentThread().getName());
}
@Override
public void onError(Throwable t) {
System.out.println("onError!" + Thread.currentThread().getName());
}
@Override
public void onComplete() {
System.out.println("onCompleted!" + Thread.currentThread().getName());
}
});
}
private static class MyServiceService {
private Repository myRepo = new Repository();
public Mono<MyUser> save(String userId) {
return myRepo.save(userId);
}
}
private static class Repository {
public Mono<MyUser> save(String userId) {
return Mono.create(myUserMonoSink -> {
Future<MyUser> submit = exe.submit(() -> this.blockingMethod(userId));
ListenableFuture<MyUser> myUserListenableFuture = JdkFutureAdapters.listenInPoolThread(submit);
Futures.addCallback(myUserListenableFuture, new FutureCallback<MyUser>() {
@Override
public void onSuccess(MyUser result) {
myUserMonoSink.success(result);
}
@Override
public void onFailure(Throwable t) {
myUserMonoSink.error(t);
}
});
});
}
private MyUser blockingMethod(String userId) throws InterruptedException {
Thread.sleep(5000);
return new MyUser("blocking", userId);
}
}
以上代码仅打印Subcribed!main。我无法弄清楚为什么未来的回调不是通过myUserMonoSink.success
答案 0 :(得分:3)
要记住的重要一点是,大多数情况下,Flux
或Mono
异步。
订阅后,保存用户的异步处理在执行程序中启动,但在.subscribe(...)
之后在主代码中继续执行。
所以main
线程退出,在将任何内容推送到Mono
之前终止测试。
[侧边栏]:什么时候同步?
当数据源是Flux / Mono同步工厂方法时。但是增加的先决条件是操作员链的其余部分不会切换执行上下文。这可能是明确地(您使用publishOn
或subscribeOn
运算符)或隐式(某些运算符,如与时间相关的运算符,例如。delayElements
)在单独的Scheduler
上运行)。
简单地说,您的源是在ExecutorService
的{{1}}个线程中运行的,因此exe
确实是异步的。另一方面,您的代码段在Mono
上运行。
如何解决问题
要在实验中观察main
的正确行为(与生产中的完全异步代码相反),可以使用以下几种方法:
Mono
,但在subscribe
和new CountDownLatch(1)
中添加.countDown()
onComplete
。 onError
之后的倒计时锁定await
。subscribe
代替.log().block()
。您将失去对每个事件执行操作的自定义,但.subscribe(...)
将为您打印出这些内容(前提是您已配置了日志框架)。 log()
将恢复为阻止模式,并完成我上面用CountDownLatch建议的内容。它返回值一旦可用,或者在出现错误时抛出block()
。Exception
您可以使用log()
方法自定义日志记录或其他副作用(对于几乎所有类型的事件+事件组合,都有自定义日志或其他副作用,例如。{{1} },.doOnXXX(...)
...)如果您正在进行单元测试,请使用doOnSubscribe
项目中的doOnNext
。当您拨打StepVerifier
时,它会订阅flux / mono并等待事件。请参阅参考指南chapter on testing(以及参考指南的其余部分)。
答案 1 :(得分:1)
问题是在创建的匿名类onSubscribe
方法中什么都不做。
如果您查看LambdaSubscriber
的实现,它会请求一些事件。
此外,它更容易扩展BaseSubscriber
,因为它有一些预定义的逻辑。
所以您的订阅者实施将是:
MyServiceService service = new MyServiceService();
service.save("id")
.map(myUserMono -> new MyUser(myUserMono.getName().toUpperCase(), myUserMono.getId().toUpperCase()))
.subscribe(new BaseSubscriber<MyUser>() {
@Override
protected void hookOnSubscribe(Subscription subscription) {
System.out.println("Subscribed!" + Thread.currentThread().getName());
request(1); // or requestUnbounded();
}
@Override
protected void hookOnNext(MyUser myUser) {
System.out.println("OnNext on thread " + Thread.currentThread().getName());
// request(1); // if wasn't called requestUnbounded() 2
}
@Override
protected void hookOnComplete() {
System.out.println("onCompleted!" + Thread.currentThread().getName());
}
@Override
protected void hookOnError(Throwable throwable) {
System.out.println("onError!" + Thread.currentThread().getName());
}
});
也许它不是最好的实施方式,我也是反应堆的新手。
Simon的回答对测试异步代码有很好的解释。