【问题标题】:How can I pass execution context in for comprehension whithout using implicit in scala?如何在不使用scala隐式的情况下将执行上下文传递给理解?
【发布时间】:2020-10-18 15:56:27
【问题描述】:

我有一个使用 for-comprehension 运行数据库查询的代码:

val totalFeeNoticeAmountFromDB = Future(/..Doing db job../)(executionContext)
val listOfRestrictedFundFromDB = Future(/..Doing db job../)(executionContext)

val res = for {
      totalFeeNoticeAmount <- totalFeeNoticeAmountFromDB
      listOfRestrictedFund <- listOfRestrictedFundFromDB
    } yield (totalFeeNoticeAmount, listOfRestrictedFund) 

我们知道,为了运行理解,我们需要传递隐式执行上下文。 但在这种情况下,我想手动传递执行上下文。
有什么办法?

已编辑:

val res = for {
          totalFeeNoticeAmount <-(?:ExecutionContext) totalFeeNoticeAmountFromDB
          listOfRestrictedFund <-(?:ExecutionContext) listOfRestrictedFundFromDB
        } yield (totalFeeNoticeAmount, listOfRestrictedFund) 

totalFeeNoticeAmountFromDBlistOfRestrictedFundFromDB 都是已启动的 Future 类型。

有没有办法通过这里 &lt;-(?:ExecutionContext)?

【问题讨论】:

  • 任何隐式参数都可以作为普通参数显式传递,但是如果同一个调用有多个隐式参数,那么所有这些参数都必须显式传递。
  • @cchantep,我知道。我编辑了我的问题。实际上我想在这个&lt;-(?:ExecutionContext) 部分中明确地传递执行上下文。
  • 我相信最简单的方法是为该部分创建一个新的块或函数,并为该块设置一个新的隐式执行上下文。
  • 其他简单的替代方案,考虑到您的未来不相互依赖,将使用您想要的明确 EC 创建它们,然后组合它们。

标签: scala future


【解决方案1】:

也许考虑scala-async,它在Scala 2.13.3 中获得了experimental compiler support -Xasync,下面是理解

for {
  a <- Future(41)
  b <- Future(1)
} yield {
  a + b
}

可以改写为

async {
  val a = async(41)(ec)
  val b = async(1)(ec)
  await(a) + await(b)
}(ec)

我们可以在执行上下文中显式传递ec 而无需借助 flatMap/map。

另一个 hacky 选项可能是 better-monadic-for,它支持在 for-comprehensions 中定义隐式

val ec: ExecutionContext = ???
(for {
  implicit0(ec: ExecutionContext) <- Future.successful(ec)
  a <- Future(41)(ec)
  b <- Future(1)(ec)
} yield {
  a + b
})(ec)

【讨论】:

  • for { a &lt;-(?:ExecutionContex) Future(41)(ec) b &lt;-(?:ExecutionContex) Future(1)(ec) } yield { a + b }我想传入这个&lt;-(?:ExecutionContex)
  • 在这种情况下,我将需要“scala-async”库。我正在投票给你。但是for-comprehension有什么办法吗?
  • @MHJ 我不相信,见stackoverflow.com/a/21256762/5205022
【解决方案2】:

你可以重写

val res = for {
  totalFeeNoticeAmount <- totalFeeNoticeAmountFromDB
  listOfRestrictedFund <- listOfRestrictedFundFromDB
} yield (totalFeeNoticeAmount, listOfRestrictedFund)

作为

val res = totalFeeNoticeAmountFromDB.flatMap(totalFeeNoticeAmount =>
  listOfRestrictedFundFromDB.map(listOfRestrictedFund =>
    (totalFeeNoticeAmount, listOfRestrictedFund)
  )
)

例如,如果 totalFeeNoticeAmountFromDBlistOfRestrictedFundFromDBFutures,那么您可以显式传递隐式 scala.concurrent.ExecutionContext.Implicits.global

val res = totalFeeNoticeAmountFromDB.flatMap(totalFeeNoticeAmount =>
  listOfRestrictedFundFromDB.map(listOfRestrictedFund =>
    (totalFeeNoticeAmount, listOfRestrictedFund)
  )(scala.concurrent.ExecutionContext.Implicits.global)
)(scala.concurrent.ExecutionContext.Implicits.global)

【讨论】:

  • @MHJ 用于压缩总是顺序而不是并行。
  • @MHJ 不,它是否依赖于前一个并不重要。它总是顺序的,因为 for comprehension 只是 flatMap 的糖语法,所以它是相同的代码。如果您在for 之前定义期货,您可以让它像并行一样运行,因为期货急于在对它们进行排序之前运行它们,但这实际上是一个错误而不是一个特性。不管怎样,你可以用 flatMap 做同样的事情
  • @LuisMiguelMejíaSuárez,是的,你是对的。问题是,如果我们在调用 for-comprehension 之前启动未来,那么在实现 for-comprehension 之前会发生平行的事情
  • @MHJ 是的,我告诉你你可以使用flatMap 做同样的事情,这样应该可以解决你的问题。
  • @MHJ 好吧,您可以创建自己的编译器插件(例如 Mario Galic 在他的回答中提到的“better-monadic-for”插件),它将扩展 for { a &lt;-(ec) fa; b &lt;-(ec1) fb } yield (a, b)进入fa.flatMap(a =&gt; fb.map(b =&gt; (a, b))(ec))(ec1),但这有点矫枉过正。这样的插件也需要修改parser 阶段,因为for { a &lt;-(ec) fa... 现在无法解析(“better-monadic-for”不会修改parser)。
【解决方案3】:

我相信解决这个问题最简单的方法就是创建一个辅助函数。

def foo(implicit ec: ExecutionContext): Future[(Int, Int)] = {
  val totalFeeNoticeAmountFromDB = Future(/..Doing db job../)
  val listOfRestrictedFundFromDB = Future(/..Doing db job../)

  for {
    totalFeeNoticeAmount <- totalFeeNoticeAmountFromDB
    listOfRestrictedFund <- listOfRestrictedFundFromDB
  } yield (totalFeeNoticeAmount, listOfRestrictedFund)
}

这样,当你需要它时,你可以这样称呼它:foo(ec = myExplicitExecutionContext)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-03-24
    • 1970-01-01
    • 2022-06-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-08
    • 1970-01-01
    相关资源
    最近更新 更多