在Play 2.5中流动未来

时间:2017-05-30 15:28:34

标签: scala playframework streaming akka akka-stream

我再一次尝试更新一些Play 2.5前代码(基于此vid)。例如,以下过去是如何流式传输 Future

  Ok.chunked(Enumerator.generateM(Promise.timeout(Some("hello"), 500)))

我使用 Akka Promise.timeout(已弃用)创建了以下方法:

  private def keepResponding(data: String, delay: FiniteDuration, interval: FiniteDuration): Future[Result] = {
    val promise: Promise[Result] = Promise[Result]()
    actorSystem.scheduler.schedule(delay, interval) { promise.success(Ok(data)) }
    promise.future
  }

根据Play Framework Migration Guide; Enumerators应该重写为来源Source.unfoldAsync显然等同于Enumerator.generateM所以我希望这会有用(strFuture[String] a def inf = Action { request => val str = keepResponding("stream me", 1.second, 2.second) Ok.chunked(Source.unfoldAsync(str)) } ):

unfoldAsync

当然,我在查看final class UnfoldAsync[S, E](s: S, f: S ⇒ Future[Option[(S, E)]]) 的案例类签名时遇到类型不匹配错误:

import React, {Component} from 'react';
import {Route, Switch} from 'react-router-dom';
import {Provider as StoreProvider} from 'react-redux';
import {ConnectedRouter} from 'react-router-redux';

const App = ({store}) => {
  return (
          <StoreProvider store={store}>
            <ConnectedRouter history={history}>
              <Switch>
                <PrivateRoute path="/welcome/:welcomeId" layout={MainLayout} component={Welcome} />
              </Switch>
            </ConnectedRouter>
          </StoreProvider>
  );
};

我可以看到参数不正确但我不完全明白应该通过什么/如何通过。

1 个答案:

答案 0 :(得分:1)

unfoldAsync比Play!自己的generateM更通用,因为它允许您传递状态(S)值。这可以使发出的值取决于先前发出的值。

下面的示例将按增加的ID加载值,直到加载失败:

val source: Source[String, NotUsed] = Source.unfoldAsync(0){ id ⇒
  loadFromId(id)
    .map(s ⇒ Some((id + 1, s)))
    .recover{case _ ⇒ None}
}

def loadFromId(id: Int): Future[String] = ???

在您的情况下,实际上并不需要内部状态,因此您可以在需要时传递虚拟值,例如

val source: Source[Result, NotUsed] = Source.unfoldAsync(NotUsed) { _ ⇒
  schedule("stream me", 2.seconds).map(x ⇒ Some(NotUsed → x))
}

def schedule(data: String, delay: FiniteDuration): Future[Result] = {
  akka.pattern.after(delay, system.scheduler){Future.successful(Ok(data))}
}

请注意,keepResponding的原始实施不正确,因为您无法多次完成Promise。 Akka after模式提供了一种更简单的方法来实现您的需求。

但请注意,在您的具体情况下,Akka Streams提供了一个更加惯用的解决方案Source.tick

val source: Source[String, Cancellable] = Source.tick(1.second, 2.seconds, NotUsed).mapAsync(1){ _ ⇒
  loadSomeFuture()
}

def loadSomeFuture(): Future[String] = ???

甚至更简单,以防您实际上不需要像示例中的异步计算

val source: Source[String, Cancellable] = Source.tick(1.second, 2.seconds, "stream me")