在 API 中返回 Futures.immediateFuture(x) 是反模式吗?

Is returning Futures.immediateFuture(x) in an API an antipattern?

这似乎没有包含在 https://github.com/google/guava/wiki/ListenableFutureExplained

但是很常见的模式

@Override
public ListenableFuture<Void> loadResources() {
  fastSyncMethod();

  return Futures.immediateFuture(null);
}

有这方面的指导吗?许多使用 ListenableFutures 的代码的使用会受到我们在使用 ListenableFuture 调用或实现 API 时可以安全做出的假设的影响。

我的 2c

对我来说似乎是一种反模式。

  • 它留下了两个用于错误处理的代码路径,调用代码中必需的 try {} catch {},以及 ListenableFuture 管道中的异常
  • 这使得syncMethod() 的执行时间与调用方是否可以采用Synchronous 中的方法有关。对于高吞吐量代码,即使是写入磁盘或使用 InetAddress api 的日志语句也是阻塞操作。
  • 它打破了期货的某种功能范式,例如参照透明度。

我们不知道 syncMethod 中的内容,但选择名称似乎是为了暗示 "some synchronous communication code",在这种情况下,您显示的代码示例显然是反模式。我怀疑它没有包含在番石榴文档中只是因为他们从没想过有人会写它。这就像告诉人们不要将他们的方法声明为 returning 一个对象,如果它实际上总是 return 为 null,或者如果它实际上总是 "doX" 则调用它 "doX" Y。签名承诺行为,并且 return 一个未来承诺你,至少有时,return 一个不完整的未来。

如果您有一种有时会延迟工作而有时不会延迟工作的方法,那么 return 在非延迟路径上完成未来是有意义的。以下是一些准则:

  1. 考虑调用者的异常处理:它可能预期某些类型的失败会导致未来失败,并且可能无法正确处理直接引发异常的方法。
  2. 不要直接调用任何引发 InterruptedException 的代码。这是一个明确的信号,表明您打破了调用者对您是否异步的期望。
  3. IOException 也是一个危险信号,因为它通常伴随着缓慢的网络或磁盘调用,而您的调用者可能希望这些调用是异步发生的。

遗憾的是,如何 没有将同步调用转换为异步调用的通用解决方案。理想情况下,您应该在引擎盖下使用异步库来进行网络调用,这样您的线程就不会在服务器之间反弹字节时四处收集灰尘。如果这不是一个选项,您将不得不启动一个线程池——只需关注您创建的线程数即可。

您的示例显示了一个被覆盖的方法,这在这里很重要。

通过让方法 return 成为 ListenableFuture,接口或超类的作者 允许 异步实现 return。同样,该方法的调用者知道可以容忍异步 return 值。

在 return 中,实现承诺是非阻塞的。因为它 return 是 ListenableFuture,所以它有机会将任何昂贵的工作转移到后台线程、使用异步网络调用或其他任何方式。

不过,它不必 运行 异步。 如果实现是非阻塞的并且将快速完成,则绝对没有理由强制执行线程切换只是为了满足工作将异步完成的一些期望。在那种情况下,immediateFuture() 是符合接口的好方法。

那么,你的例子是反模式吗?这取决于 syncMethod() 的价格。如果它很快(非阻塞,并且不做一些冗长的计算)那么这个例子很好。如果它很慢(也许它在网络上等待,或者分解一个非常大的数字)那么是的,它是一个反模式。