Play 2.0 Scala и асинхронные обратные вызовы

Я хочу сделать два вызова в свою базу данных, что займет некоторое время, чтобы вернуть результат, и я не хочу блокировать текущий поток. Я использовал Akka Futures для переноса вызовов базы данных.

Вместо ожидания (блокировки) возврата обоих вызовов я хотел бы указать вызываемую функцию обратного вызова, которая затем может отображать ответ. Как я могу это сделать? Вот мой код контроллера:

def showPie = IsAuthenticated(Roles.validator) { user => implicit request =>
    val eventUid = request.session.get(EventUid).get

    val printed = Akka.future(TicketRepository.getCountForState(eventUid, "Printed"))
    val validated = Akka.future(TicketRepository.getCountForState(eventUid, "Validated"))

    //this would be evil, because it would block: Ok(views.html.pie(printed.await(1000).get, validated.await(1000).get)) 

    //create a promise for all the promised results
    val promise = Promise.sequence(List(printed, validated))

    //this doesnt work, but how can I make it work WITHOUT blocking this thread?
    promise.callWhenResultIsReady(Ok(view.html.pie(promise.get))
}

person John Smith    schedule 25.09.2012    source источник


Ответы (1)


Вы близко. Вы можете просто позвонить map по обещанию разобраться с этим. Внутри асинхронного блока он остается неблокирующим. Соответствующая документация (см. "AsyncResult").

def showPie = IsAuthenticated(Roles.validator) { user => implicit request =>
    val eventUid = request.session.get(EventUid).get

    val printed = Akka.future(TicketRepository.getCountForState(eventUid, "Printed"))
    val validated = Akka.future(TicketRepository.getCountForState(eventUid, "Validated"))

    //create a promise for all the promised results
    val promise = Promise.sequence(List(printed, validated))
    Async {
        promise map { res =>
            Ok("Got it!" + res)
        }
    }
}

изменить: Из вашего комментария ниже давайте подробнее рассмотрим блок Async. Async принимает Promise и возвращает AsyncResult, который является подтипом Result (это то, что нужно Action).

    Async {
        // We take the promise, and add something akin to a callback
        //  function with `map`. This new function is called when `promise`
        //  is complete.
        val result = promise map { res => // this is the redeemed promise
          Ok("Got it!" + res)
        }
        result // this is the new promise
    } 

Поскольку функция map вызывается после завершения promise, это остается неблокирующим. Весь этот блок быстро возвращается с AsyncResult, и Play! управляет им аналогичным образом, возвращаясь к клиенту, когда он завершается (и освобождая Play!, чтобы тем временем заниматься другими делами).

person Andrew Conner    schedule 25.09.2012
comment
Вы знаете, как это работает под капотом? Время от времени опрашивается Promise, чтобы узнать, готово ли оно, а если нет, Акка идет и выполняет какую-то другую работу? - person John Smith; 25.09.2012
comment
Лучше, чем это. Поскольку ваши вызовы выполняются в будущем, они добавляются в очередь сообщений актора за кулисами, и, когда она становится доступной (обычно очень быстро), она обрабатывает ее, а затем вызывает любые функции завершения. В Akka так работают акторы map, onComplete, onSuccess и onFailure. Ваше последнее обещание (последовательность) просто оборачивает другие и выполняет функции прослушивания, когда каждая часть завершена. Поскольку все это происходит в обратных вызовах, опрос не требуется. - person Andrew Conner; 25.09.2012
comment
Вызов map возвращает еще одно обещание, которое Async с радостью принимает в качестве возвращаемого значения литерала функции. Затем он добавляет метод слушателя к который обещает вернуть ваше сообщение клиенту. Весь процесс оказывается неблокирующим и не требует опроса. - person Andrew Conner; 25.09.2012