当前位置: 首页 > 知识库问答 >
问题:

使用“回滚”代码平面映射未来

程旭尧
2023-03-14

我很难找到一个优雅的解决方案来链接一些期货。我试图实现的方法看起来像这样(它是缓存的一部分):

def acquire(key: A, producer: => Future[B]): Future[B]

算法大致如下:

  • 如果密钥被锁定,则立即通过运行时异常(使用 Future.failed 是否会提前?
  • 否则打开一个将来的 { 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>完成的对等未来,并返回对等结果,但如果失败,请在完成主未来之前清除锁。

共有2个答案

夏才
2023-03-14

我对您的实现做了一些更改。

首先,我已经将您最初的<code>抛出锁定到<code>未来。失败,因为期货消费者应该可以安全地认为,唯一需要注意的失败是失败的未来。

接下来,我不是在readEntryOption[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
      }
    }
  }
}
衡建中
2023-03-14

这里尝试使用 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 类型未定义此处适用的。 我做错了什么?