scala 将隐式 ExecutionContext 传递给包含的对象/调用的方法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17849749/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 05:32:36  来源:igfitidea点击:

Passing implicit ExecutionContext to contained objects/called methods

scalascala-2.10executioncontext

提问by Michelle Tilley

I'm creating an async library using Scala 2.10 futures. The constructor for the library takes a sequence of user-defined objects that implement a certain trait, and then a method on the library class sends some data one-by-one into the user-defined objects. I want the user to provide the ExecutionContextfor the async operations when setting up the main instance, and then for that context to get passed into the user-defined objects as necessary. Simplified (pseudo?)code:

我正在使用 Scala 2.10 期货创建一个异步库。库的构造函数采用一系列实现特定特征的用户定义对象,然后库类上的方法将一些数据一个一个地发送到用户定义对象中。我希望用户ExecutionContext在设置主实例时提供异步操作,然后根据需要将该上下文传递到用户定义的对象中。简化(伪?)代码:

case class Response(thing: String)

class LibraryObject(stack: Seq[Processor])(implicit context: ExecutionContext) {
  def entryPoint(data: String): Future[Response] = {
    val response = Future(Response(""))
    stack.foldLeft(response) { (resp, proc) => proc.process(data, resp) }
  }
}

trait Processor {
  def process(data: String, resp: Future[Response]): Future[Response]
}

It might be used something like this:

它可能会被这样使用:

class ThingProcessor extends Processor {
  override def process(data: String, response: Future[Response]) = {
    response map { _.copy(thing = "THE THING") }
  }
}

class PassThroughProcessor extends Processor {
  override def process(request: Request, response: Future[Response]) = {
    response
  }
}

object TheApp extends App {
  import ExecutionContext.Implicits.global

  val stack = List(
    new ThingProcessor,
    new PassThroughProcessor
  )
  val libObj = new LibraryObject(stack)

  val futureResponse = libObj.entryPoint("http://some/url")

  // ...
}

I get a compile error for ThingProcessor:

我收到以下编译错误ThingProcessor

Cannot find an implicit ExecutionContext, either require one yourself or import ExecutionContext.Implicits.global

找不到隐含的 ExecutionContext,要么自己需要,要么导入 ExecutionContext.Implicits.global

My question is, how do I implicitly supply the ExecutionContextthat LibraryObjecthas to the user-defined objects (ThingProcessorand PassThroughProcessor) or their methods without making the user (who will be writing the classes) worry about it--that is to say, I would prefer that the user did nothave to type:

我的问题是,我怎么隐含提供ExecutionContext的是LibraryObject具有对用户定义的对象(ThingProcessorPassThroughProcessor)或它们的方法不使用户(谁将会被写入类)担心-这就是说,我宁愿用户不必键入:

class MyFirstProcessor(implicit context: ExecutionContext)

or

或者

override def process(...)(implicit context: ExecutionContext) = { ... }

采纳答案by som-snytt

The implicit scope includes companion objects and type parameters of base classes.

隐式范围包括伴随对象和基类的类型参数。

Or, library.submit(new library.Processor { def process() ... }).

或者, library.submit(new library.Processor { def process() ... })。

This works, but wasn't my first thought, which was to be more clever:

这有效,但不是我的第一个想法,那就是更聪明:

import concurrent._
import concurrent.duration._

class Library(implicit xc: ExecutionContext = ExecutionContext.global) {
  trait Processor {
    implicit val myxc: ExecutionContext = xc
    def process(i: Future[Int]): Future[Int]
  }

  def submit(p: Processor) = p process future(7)
}

object Test extends App {
  val library = new Library
  val p = new library.Processor {
    def process(i: Future[Int]) = for (x <- i) yield 2 * x
  }
  val res = library submit p
  val z = Await result (res, 10.seconds)
  Console println z
}

Update:

更新:

import concurrent._
import concurrent.duration._
import java.util.concurrent.Executors

class Library()(implicit xc: ExecutionContext = ExecutionContext.global) {
  trait Processor {
    implicit val myxc: ExecutionContext = xc
    def process(i: Future[Int]): Future[Int]
  }

  def submit(p: Processor) = p process future(7)
}

object ctx {
  val xc = ExecutionContext fromExecutorService Executors.newSingleThreadExecutor
}
object library1 extends Library
object library2 extends Library()(ctx.xc)
object p extends library1.Processor {
  def process(i: Future[Int]) = for (x <- i) yield 2 * x
}
object q extends library2.Processor {
  def process(i: Future[Int]) = for (x <- i) yield 3 * x
}

object Test extends App {
  val res = library1 submit p
  //val oops = library2 submit p
  //val oops = library1 submit q
  val z = Await result (res, 10.seconds)
  Console println z
  Console println (Await result (library2 submit q, 10.seconds))
  ctx.xc.shutdownNow()
}

It isn't much of a stretch to:

这并不难:

class Library(implicit xc: ExecutionContext = ExecutionContext.global) {

  def submit(p: Processor): Future[Int] = p dueProcess future(7)
}
trait Processor {
  implicit var myxc: ExecutionContext = _
  def dueProcess(i: Future[Int])(implicit xc: ExecutionContext) = {
    myxc = xc
    process(i)
  }
  protected def process(i: Future[Int]): Future[Int]
}

object ctx {
  val xc = ExecutionContext fromExecutorService Executors.newSingleThreadExecutor
}
object Test extends App {
  def db() = Console println (new Throwable().getStackTrace mkString ("TRACE [\n  ", "\n  ", "\n]"))
  val library = new Library()(ctx.xc)
  val p = new Processor {
    protected def process(i: Future[Int]) = for (x <- i) yield { db(); 2 * x }
  }
  val res = library submit p
  val z = Await result (res, 10.seconds)
  Console println z
  ctx.xc.shutdownNow()
}