2014-07-13 21 views
5

Jestem nowy w programowaniu asynchronicznym. Przeczytałem ten samouczek: http://danielwestheide.com/blog/2013/01/09/the-neophytes-guide-to-scala-part-8-welcome-to-the-future.html i byłem zaskoczony, jak łatwo mogę włączyć Future do programu. Jednak, gdy korzystałem z Future z Routingiem, typ zwrotu jest nieprawidłowy.Użyj przyszłości w routingu natryskowym

get { 
    optionalCookie("commToken") { 
    case Some(commCookie) => 
     val response = (MTurkerProgressActor ? Register).mapTo[..].map({...}) 
     val result = Await.result(response, 5 seconds) 

     setCookie(HttpCookie("commToken", content = result._2.mturker.get.commToken)) { 
     complete(result._1, result._2.mturker.get) 
     } 

    case None => // ... 
    } 
} 

ja naprawdę nie chcę użyć Await (co jest punktem asynchroniczny gdybym tylko zablokować wątek i poczekaj 5 sekund?). Próbowałem użyć for -rozumienie lub flatMap i umieścić wewnątrz działań setCookie i complete, ale typ zwracany jest nie do zaakceptowania w Spray. For-Comprehension zwraca "Unit", a flatMap zwraca Przyszłość.

Ponieważ muszę ustawić ten plik cookie, potrzebuję danych w środku. Czy rozwiązanie to jest Await? A może jest jakaś smatterowa droga?

+0

'' flatmap' map' i są zwykle droga. Wyrażenie 'for' zwraca tylko' Unit', jeśli używasz go bez 'yield'. Gdy użyjesz 'yield', zostanie on przekształcony w wyrażenie używając' flatMap' i 'map'. – jrudolph

Odpowiedz

15

Można użyć dyrektywy onSuccess:

get { 
    optionalCookie("commToken") { cookie => 
     //.... 
     val response = (MTurkerProgressActor ? Register).mapTo[..].map({...}) 
     onSuccess(response) { 
     case (result, mTurkerResponse) => 
      setCookie(HttpCookie("commToken", content = mTurkerResponse.mturker.get.commToken)) { 
      complete(result, mturkerResponse.mturker.get) 
      } 
     } 
    } 

Jest też onFailure i onComplete (dla których trzeba dopasować na Success i Failure) Zobacz http://spray.io/documentation/1.2.1/spray-routing/future-directives/onComplete/

Ponadto, zamiast korzystać get bezpośrednio to dużo bardziej idiomatyczny w użyciu map (Zakładam, że mturker to Option lub coś podobnego):

case (result, mTurkerResponse) => 
    mTurkerResponse.mturker.map { mt => 
    setCookie(HttpCookie("commToken", content = mt.commToken)) { 
     complete(result, mt) 
    } 
    } 
+0

To powinna być zaakceptowana odpowiedź. Dzięki Mario! – longliveenduro

+1

Uwaga: Musisz mieć pewność, że obiekt ExecutionContext znajduje się w zakresie lub otrzymujesz błąd TypeError podczas próby wywołania onSuccess (...). –

+0

Wreszcie działa! O dziwo, działa to tylko dla mnie z 'onSuccess'. Kiedy próbuję dodać 'onFailure', łamie się. :/ –

1

Można również dyrektywę niestandardowej przy użyciu tego kodu -

case class ExceptionRejection(ex: Throwable) extends Rejection 

protected def futureDirective[T](x: Future[T], 
           exceptionHandler: (Throwable) => Rejection = ExceptionRejection(_)) = 
    new Directive1[T] { 
    override def happly(f: (::[T, HNil]) => Route): Route = { ctx => 
     x 
     .map(t => f(t :: HNil)(ctx)) 
     .onFailure { case ex: Exception => 
      ctx.reject(exceptionHandler(ex)) 
     } 
    } 
    } 

Przykład użycia -

protected def getLogin(account: Account) = futureDirective(
    logins.findById(account.id) 
) 


getAccount(access_token) { account => 
    getLogin(account) { login => 
    // ... 
    } 
} 
+0

:: [T, HNil] daje mi błąd "niepoprawna liczba parametrów" – user2975535