简体   繁体   English

Scala 未来的 flatMap 实现(链接)

[英]Scala Future flatMap implementation (chaining)

Trying to wrap my head around how async tasks are chained together, for ex futures and flatMap试图了解异步任务是如何链接在一起的,对于 ex futures 和flatMap

val a: Future[Int] = Future { 123 }
val b: Future[Int] = a.flatMap(a => Future { a + 321 })

How would i implement something similar where i build a new Future by waiting for a first and applying a f on the result.我将如何通过等待第a并在结果上应用f来构建一个新的Future来实现类似的东西。 I tried looking a scala's source code but got stuck here: https://github.com/scala/scala/blob/2.13.x/src/library/scala/concurrent/Future.scala#L216我尝试查看 scala 的源代码,但卡在这里: https://github.com/scala/scala/blob/2.13.x/src/library/scala/concurrent/Future.scala#L216

i would imagine some code like:我会想象一些代码,例如:

def myFlatMap(a: Future[Int])(f: a => Future[Int]): Future[Int] = {
   Future {
      // somehow wait for a to complete and the apply 'f'
      a.onComplete {
        case Success(x) => f(a)
      }
   }
}

but i guess above will return a Future[Unit] instead.但我猜上面会返回一个 Future[Unit] 。 I would just like to understand the "pattern" of how async tasks are chained together.我只想了解异步任务如何链接在一起的“模式”。

(Disclaimer: I'm the main maintainer of Scala Futures) (免责声明:我是Scala期货的主要维护者)

You wouldn't want to implement flatMap/recoverWith/transformWith yourself—it is a very complex feature to implement safely (stack-safe, memory-safe, and concurrency-safe).你不会想自己实现 flatMap/recoverWith/transformWith ——这是一个非常复杂的功能,要安全地实现(堆栈安全、内存安全和并发安全)。

You can see what I am talking about here:你可以在这里看到我在说什么:

There is more reading on the topic here: https://viktorklang.com/blog/Futures-in-Scala-2.12-part-9.html这里有更多关于这个主题的阅读: https://viktorklang.com/blog/Futures-in-Scala-2.12-part-9.html

I found that looking at the source for scala 2.12 was easier for me to understand how Future / Promise was implemented, i learned that Future is in fact a Promise and by looking at the implementation i now understand how "chaining" async tasks can be implemented.我发现查看 scala 2.12的源代码对我来说更容易理解Future / Promise是如何实现的,我了解到Future实际上是一个Promise的实现方式,现在可以通过查看“链式 i 实现”来了解如何实现异步任务.

Im posting the code that i wrote to help me better understand what was going on under the hood.我发布了我编写的代码,以帮助我更好地了解引擎盖下发生的事情。

import java.util.concurrent.{ScheduledThreadPoolExecutor, TimeUnit}
import scala.collection.mutable.ListBuffer

trait MyAsyncTask[T] {
  type Callback = T => Unit
  var result: Option[T]

  // register a new callback to be called when task is completed.
  def onComplete(callback: Callback): Unit
  // complete the given task, and invoke all registered callbacks.
  def complete(value: T): Unit

  // create a new task, that will wait for the current task to complete and apply
  // the provided map function, and complete the new task when completed.
  def flatMap[B](f: T => MyAsyncTask[B]): MyAsyncTask[B] = {
    val wrapper = new DefaultAsyncTask[B]()
    onComplete { x =>
      f(x).onComplete { y =>
        wrapper.complete(y)
      }
    }

    wrapper
  }

  def map[B](f: T => B): MyAsyncTask[B] = flatMap(x => CompletedAsyncTask(f(x)))
}

/**
  * task with fixed pre-calculated result.
  */
case class CompletedAsyncTask[T](value: T) extends MyAsyncTask[T] {
  override var result: Option[T] = Some(value)
  override def onComplete(callback: Callback): Unit = {
    // we already have the result just call the callback.
    callback(value)
  }

  override def complete(value: T): Unit = () // noop nothing to complete.
}

class DefaultAsyncTask[T] extends MyAsyncTask[T] {
  override var result: Option[T] = None
  var isCompleted = false
  var listeners = new ListBuffer[Callback]()

  /**
    * register callback, to be called when task is completed.
    */
  override def onComplete(callback: Callback): Unit = {
    if (isCompleted) {
      // already completed just invoke callback
      callback(result.get)
    } else {
      // add the listener
      listeners.addOne(callback)
    }
  }

  /**
    * trigger all registered callbacks to `onComplete`
    */
  override def complete(value: T): Unit = {
    result = Some(value)
    listeners.foreach { listener =>
      listener(value)
    }
  }
}

object MyAsyncTask {
  def apply[T](body: (T => Unit) => Unit): MyAsyncTask[T] = {
    val task = new DefaultAsyncTask[T]()
    // pass in `complete` as callback.
    body(task.complete)

    task
  }
}

object MyAsyncFlatMap {

  /**
    * helper to simulate async task.
    */
  def delayedCall(body: => Unit, delay: Int): Unit = {
    val scheduler = new ScheduledThreadPoolExecutor(1)
    val run = new Runnable {
      override def run(): Unit = {
        body
      }
    }

    scheduler.schedule(run, delay, TimeUnit.SECONDS)
  }

  def main(args: Array[String]): Unit = {
    val getAge = MyAsyncTask[Int] { cb =>
      delayedCall({ cb(66) }, 1)
    }

    val getName = MyAsyncTask[String] { cb =>
      delayedCall({ cb("John") }, 2)
    }

    // same as: getAge.flatMap(age => getName.map(name => (age, name)))
    val result: MyAsyncTask[(Int, String)] = for {
      age <- getAge
      name <- getName
    } yield (age, name)

    result.onComplete {
      case (age, name) => println(s"hello $name $age")
    }
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM