2014-02-12 3 views
0

У меня есть сценарий, в котором я получаю сообщение List of String, и мне нужно выполнить итерацию по String и вызвать другой метод, который является долговременным процессом. Затем я должен собрать результаты этого долгого процесса и объединить результаты и отправить их обратно в пользовательский интерфейс. Я довольно новичок в этих концепциях Future в Scala. Я использую Play framework, где-в списке Strings будет создан пользовательский интерфейс. Вот как моя первая попытка реализации сценария ХТ выглядит:Akka Future - Необходимые предложения

def futuresTest(strList: List[String]) = Action { 
    Async { 

    val ftrList: List[Future[String]] = 
     strList.map(s => Akka.future {longRunningCall(s)}).toList 

    val futureResultList = Future.sequence(ftrList) 

    val jsonResponse: String = 
     futureResultList.map(_.sum).asInstanceOf[String] 

    Akka.future { Ok(jsonResponse) } 
    } 
} 

Для простоты longRunningCall просто возвращает строку. Позже я привяжу его к первоначальной реализации.

def longRunningCall(s: String) = "test" 

Мой вопрос в том, что в заявлении:

val ftrList: List[Future[String]] = 
    strList.map(s => Akka.future {longRunningCall(s)}).toList 

Я бы предположил, что ftrList будет заполняться асинхронно, и когда он попадает в следующую строку, я гарантировал, что futureResultList будет содержать все элементы (т.е. strList и размер futureResultList будут равны?

val futureResultList = Future.sequence(ftrList) 

Пожалуйста консультация!

+0

При вызове '.map' на' list', он возвращает 'list' ... Вам не нужно называть' .toList' на результат. –

+0

Я также очень удивлен, увидев, что '.sum' работает над' List [String] 'и' Future', приложенным непосредственно к 'String', по-видимому, без проблем. –

+0

Это была ошибка. Сумма была ошибкой от меня. Однако IntelliJ ничего не жаловался! – sparkr

ответ

2

Я собираюсь предположить здесь, что вы имеете в виду, чтобы строки были объединены. Сначала несколько замечаний по коду:

  • Там нет необходимости, чтобы обернуть весь блок в качестве Async, только окончательное будущее, которое вы возвращаете.

  • Типы значений могут быть выведены, вам не нужно изложить их явно

  • отображение а List Возвращает List. Вызов toList по результату является излишним.

  • List#sum только работает с числами, используйте foldLeft вместо строк.

  • Future[String] может не быть отлиты непосредственно к String с помощью asInstanceOf. В любом случае вы будете возвращать его как Future.

  • map и Future.sequence могут быть объединены в одну операцию Future.traverse.

И изменить код, чтобы применить эти пункты:

def futuresTest(strList: List[String]) = Action { 

    val ftrList = Future.traverse(strList) { 
    s => Future(longRunningCall(s)) 
    } 

    // this will be a Future[String] 
    val jsonResponse = ftrList map { _.foldLeft("")(_ + _) } 

    Async { jsonResponse map (OK(_)) } 
} 

Последние две линии также могут быть объединены:

Async { 
    ftrList map { xs => OK(xs.foldLeft("")(_ + _)) } 
    } 

UPDATE

Вот то же самое используя Future.fold, как было предложено Виктором

def futuresTest(strList: List[String]) = Action {  
    val ftrList = strList map { longRunningCall(_) } // List[Future] 
    val jsonResponse = Future.fold(ftrList)("")(_ + _) // Future[String] 
    Async { jsonResponse map (OK(_)) } 
} 

Для обработки неудачи, вы хотите восстановить будущее и он возвращает другой ответ:

Async { jsonResponse map (OK(_)) recover (InternalServerError(_.toString)) } 

Если вы хотите обрабатывать отдельные ошибки от каждого элемента, то вы должны смотреть на технику используется в this answer.

+0

Асинхронный {Ok (jsonResponse)} - Это. не будет компилироваться, поскольку я должен преобразовать это Будущее [String] в результат! – sparkr

+0

Правильно, это должно быть «Будущее [Ответ]», а не ответ, содержащий будущее. Будет обновлен ответ. –

+0

Другой вопрос: будет ли ftrList содержать все ответы для всех элементов в strList? Должен ли я рассмотреть возможность добавления обработчика onComplete? Какие-либо предложения? – sparkr

0

То, что вы делаете, будет работать правильно, если LongRunningCall будет правильно возвращать значения. Размер ftrList будет равен размеру strList, если исключение из longRunningCall

+0

Может случиться так, что длительный вызов может вызвать исключение. Что мне делать в этом случае? Должен ли я соответствовать случаю? – sparkr

+0

Akka.future {longRunningCall (s)} {восстановить случай е: Exception => {e.getLocalizedMessage()}} это возвращает сообщение об ошибке вместо строки результата –

0

Мне кажется, что для вас было бы намного проще использовать параллельные коллекции Scala, а не Futures. Используя .par.map, вы можете выполнять длительные операции над каждым элементом в списке параллельно и собирать результаты вместе.

def getResponse(strList: List[String]) = Action { 

    val results: List[String] = strList.par.map(longRunningCall(_)) 
    val jsonResponse: String = results.mkString(",") //concatenate using ',' 

    Ok(jsonResponse) 
} 

http://docs.scala-lang.org/overviews/parallel-collections/configuration.html

Смежные вопросы