如何调用一个接一个地返回Future的回调列表

时间:2015-09-12 21:24:59

标签: scala future

我需要一种机制来异步调用多个回调...所以我实现了以下类:

class AsyncCallbacks[T] {

  private val callbacks = new ListBuffer[T => Future[Unit]]()

  def +=(f: T => Future[Unit]) = callbacks += f
  def -=(f: T => Future[Unit]) = callbacks -= f

  def invoke(data: T) = Future.sequence(callbacks.map(_(data)))
}

...

def f1(i: Int) = Future { println(i) }
def f2(i: Int) = Future { println(i) }

val callbacks = new AsyncCallbacks[Int]
callbacks += f1
callbacks += f2
callbacks.invoke(5)

callbacks.invoke生成scala.concurrent.Future[scala.collection.mutable.ListBuffer[Unit]] ...我想知道是否有一种更好,更有效的方法来调用所有已注册的回调,而不会生成Unit s的无用列表

上面的实现还有另一个问题......让我们假设我们有以下方法......

def l1 = Future { List.fill(5)("1") }
def l2 = Future { List.fill(5)("2") }

...然后我像这样调用它们:

for {
  a <- l1
  b <- l2
  c <- callbacks.invoke(5)
} yield b

callbacks.invoke有效...但看起来它永远不会返回...

修改

好的,我已按照I.K.的建议尝试使用scalaz重新实现我的AsyncCallbacks课程:

import scala.collection.mutable.ListBuffer
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import scalaz.concurrent.Task

class AsyncCallbacks[T] {

  private val tasks = new ListBuffer[Task[T => Future[Unit]]]()

  /** Gets the number of callbacks registered. */
  def count = tasks.length

  /** Clears all the registered callbacks. */
  def clear = tasks.clear

  /* Adds the specified function to the list of callbacks to be invoked. */
  def +=(f: T => Future[Unit]) = tasks += Task(f)

  /** Invokes all the registered callbacks. */
  def invoke(data: T) = Future { Task.gatherUnordered(tasks).map(_.map(_(data))).run.length }
}

这是它的用法:

def f1(i: Int) = Future { println(i) }
def f2(i: Int) = Future { println(i) }

val callbacks = new AsyncCallbacks[Int]()
callbacks += f1
callbacks += f2
callbacks.invoke(4) // prints 4 two times (f1 + f2)

现在只需从REPL执行上面的代码...然后尝试多次调用`callbacks.invoke(4),你会发现你不再能够退出REPL(它仍然被阻止,你必须用CTRL-C退出。我认为这可能是真实应用中的一个问题。

1 个答案:

答案 0 :(得分:1)

从您的帖子中可以看出,无论您想要将Future的主体放入Task的正文中,您希望它完成并通知您。

在Scalaz中,它将被建模为Future,其下面基本上是scala> import scalaz.concurrent.Task import scalaz.concurrent.Task scala> val tasks = (1 |-> 5).map(n => Task { Thread.sleep(100); n }) tasks: List[scalaz.concurrent.Task[Int]] = List(scalaz.concurrent.Task@72b64eae, scalaz.concurrent.Task@3f6a6af, scalaz.concurrent.Task@5ba0314c, scalaz.concurrent.Task@36718c9f, scalaz.concurrent.Task@767277c1) scala> Task.gatherUnordered(tasks).run res10: List[Int] = List(4, 1, 2, 3, 5) scala> Task.gatherUnordered(tasks).run res11: List[Int] = List(3, 1, 2, 4, 5) scala> Task.gatherUnordered(tasks).run res12: List[Int] = List(2, 1, 3, 4, 5) ,但附带了其他功能。

一些例子,

Task

如您所见,每次完成这些任务的运行时,输出都是不同的。 scala> val tasks = List(Task{1},Task{2}) tasks: List[scalaz.concurrent.Task[Int]] = List(scalaz.concurrent.Task@2858b10a, scalaz.concurrent.Task@3782f5d8) scala> Task.gatherUnordered(tasks).run res13: List[Int] = List(1, 2) scala> val tasks = List(Task{List.fill(5)("1")}, Task{List.fill(5)("2")}) tasks: List[scalaz.concurrent.Task[List[String]]] = List(scalaz.concurrent.Task@1c8dd945, scalaz.concurrent.Task@71f8e5ff) scala> Task.gatherUnordered(tasks).run res17: List[List[String]] = List(List(1, 1, 1, 1, 1), List(2, 2, 2, 2, 2)) 实施是不确定的。

举个例子,

<div class = "row" id = "activityRow">    
    <select class="combobox" id = "activityCombobox" onclick="addButtons()">
        <option value="" selected="true" style = "display:none;">-- Choose your Activity --</option>
        <option value="val1" >Val1</option>
        <option value="val2">Val2</option>
        <option value="val3">Val3</option>
        <option value="val4">Val4</option>
    </select>
</div>

 <div class="row" id = "addonRow">

 </div>