将 scala Futures 与 JavaFX 等单线程工具包一起使用的最佳方法是定义一个执行器,允许您在 UI Toolkit 的线程上执行期货或参与者。
Swing 也存在同样的问题,它也需要在 swing 线程上进行更新。Viktor Klang 提出了以下解决方案Swing Execution Context。这里是为 JavaFX 翻译的:
import akka.dispatch.ExecutionContext
import javafx.application.Platform
import java.util.concurrent.Executor
//
object JavaFXExecutionContext {
implicit val javaFxExecutionContext: ExecutionContext = ExecutionContext.fromExecutor(new Executor {
def execute(command: Runnable): Unit = Platform.runLater(command)
})
}
你会像这样使用它:
// import the default ec
import scala.concurrent.ExecutionContext.Implicits.global
// define the JavaFX ec so we can use it explicitly
val fxec = JavaFXExecutionContext.javaFxExecutionContext
future {
// some asynchronous computation, running on the default
// ForkJoin ExecutionContext because no ec is passed
// explicitly
}.map(result => {
// update JavaFX components from result
// This will run in the JavaFX thread.
// Check Platform.isFxApplicationThread() to be sure!
})(fxec)
只要与 JavaFX 组件交互的步骤都在 JavaFX ExecutionContext 上运行,future 的管道可能非常复杂。
注意:是否将 ForkJoin ec 设为默认值并显式传递 JavaFX ec 取决于您,反之亦然。将 JavaFX ec 作为默认设置以防止错误并标记可以使用 ForkJoin ec 显式异步运行的部分可能是一个好主意。
为了将基于 Actor 的系统与单线程 UI 工具包集成,还有一个解决方案。请参阅摇摆演员。您所要做的就是更换
SwingUtilities.invokeLater(command)
和
Platform.runLater(command)
你可以走了!
如果您有一个大型 UI 应用程序,并且只想分离一些异步操作(加载文件或进行一些计算),那么基于期货的方法可能更可取。但请注意不要与在默认执行上下文上异步运行的期货中的 JavaFX 组件进行任何交互(既不读取也不写入)。
如果您有一个基于actor的大型系统并且只想将UI附加到某些部分,那么在JavaFX线程上运行一些actor可能更可取。YMMV。