我很难找到一个优雅的解决方案来链接一些期货。我试图实现的方法看起来像这样(它是缓存的一部分):
def acquire(key: A, producer: => Future[B]): Future[B]
算法大致如下:
一个将来的 { getOrRefresh }
块,因为它需要一些时间来检索密钥getOrRefresh
要么返回一个直B
,然后返回未来和方法的结果生产者
最后一步意味着我需要从未来内部对未来进行“扁平化”。也就是说,我不能做一个outer.flatMap
,所以我想策略是使用等待
。
现在,< code>Await有一种奇怪的分裂,即您可以使用方法< code>ready获得< code >选项[Try[B]],或者使用< code>result获得展开的< code>B。问题是,在完成外部未来之前,我需要在< code>Failure的情况下释放一个锁,所以我想我必须坚持< code>Await.ready。
这很难看:
val fut = producer
val prod = Await.ready(fut, Duration.Inf).value.get
if (prod.isFailure) sync.synchronized { locked = false }
prod.get
这会有那么难看吗?必须有更好的方法来做到这一点。
因此,重复一下:从<code>未来[B]中运行一些同样使用<code>B<code>完成的对等未来,并返回对等结果,但如果失败,请在完成主未来之前清除锁。
我对您的实现做了一些更改。
首先,我已经将您最初的<code>抛出锁定到<code>未来。失败,因为期货消费者应该可以安全地认为,唯一需要注意的失败是失败的未来。
接下来,我不是在readEntry
的Option[B]
上调用get
,而是将其作为未来的结果返回。然后我平面地图
结果,以便我可以在无
的情况下替换生产者
产生的未来(map将导致Future[Future[B]]])。在一些
的情况下,我从值返回Future.successful
,因为平面地图
需要返回未来。
最后,我已经将恢复
和抛出
替换为然后
,因为我们希望失败的Future
传播,并且只想将副作用链接到未来以解锁条目。
trait Cache[A, B] {
class Entry(var locked: Boolean = true)
private var map = Map.empty[A, Entry]
private val sync = new AnyRef
def readEntry(key: A): Option[B] = ???
def acquire(key: A, producer: => Future[B]): Future[B] = sync.synchronized {
map.get(key) match {
case Some(e) =>
if (e.locked)
Future.failed(new IllegalStateException())
else {
e.locked = true
future { readEntry(key)}.flatMap {
case None => producer.andThen {
case Failure(_) => sync.synchronized(e.locked = false)
}
case Some(value) => Future.successful(value)
}
}
case _ => producer.map { value =>
sync.synchronized {
map += key -> new Entry()
}
value
}
}
}
}
这里尝试使用 recover(With),
从而避免等待
。不过,它看起来很笨拙,因为我需要重新抛出这个例外。
import concurrent._
trait Cache[A, B] {
class Entry(var locked: Boolean = true)
private var map = Map.empty[A, Entry]
private val sync = new AnyRef
implicit def exec: ExecutionContext
def readEntry(key: A): Option[B]
def acquire(key: A, producer: => Future[B]): Future[B] = sync.synchronized {
map.get(key) match {
case Some(e) =>
if (e.locked) throw new IllegalStateException()
e.locked = true
val existing = future { readEntry(key).get }
val refresh = existing.recoverWith {
case _: NoSuchElementException => producer
}
refresh.recover {
case t => sync.synchronized(e.locked = false); throw t
}
case _ => producer.map { value =>
sync.synchronized {
map += key -> new Entry()
}
value
}
}
}
}
如果您有建议,请将它们作为单独的答案发布。
我有一个包含240个项目的列表,使用完整发送此列表需要1个多小时。 所以我跟着这篇文章同时发送,以尽量减少响应时间,但是里面的代码永远不会执行:
前端将这个json发送到我的API 控制器:
我正在咨询你,因为我正在处理一些关于VSCODE的困扰我的事情。 我们目前正在创建一个应用程序,它应该在本地运行,所以我们的代码中确实有很多base64行。可以想象,这些字符串相当大: 这对我(和我的同事)来说是个问题,因为我们对这种轻量级的“Visual Studio”式编辑器很感兴趣,但又无法避免出现断行,这使得导航某些脚本非常复杂。
我对webflux比较陌生,我想找到解决方案,在有条件时避免嵌套flatMap: 如何防止这种分支条件平面映射混乱?我无法想象如果我在项目中有另一个实体。会有更多嵌套的平面地图?
我试图在我的gradle项目中设置静态编程语言/JS子模块,并面临GoogleChrome无法加载NPE错误的源地图的问题 这是模块的构建。格拉德尔。kts 有人能告诉我哪里错了吗?
此代码在日食火星中编译良好,但在日食月神中获得以下错误: 类型不匹配:无法从< code >映射转换 如果我没有将返回的分配给具有< code>Map的< code>Map 类型未定义此处适用的。 我做错了什么?