2016-05-20 3 views
2

Sagen wir, ich habe eine FunktionWie man eine Asynchron-Funktion mehrmals nacheinander, bis der erste Erfolg auszuführen

def request(url: String): Future[String] 

und brauchen eine Funktion

def requestFirst(urls: List[String]): Future[String] 

zu schreiben, die Anfrage ruft (url) in Sequenz, bis sie erfolgreich abgeschlossen wird (in diesem Fall wird der erfolgreiche Wert zurückgegeben) oder Anforderungen für alle URLs fehlschlagen (in diesem Fall werden alle Fehler zurückgegeben).

Wie kann dies in Scala getan werden?

+3

Könnten Sie bitte beschreiben etwas, was Sie erreichen wollen? Wenn Sie auf das Ergebnis warten müssen, ist es keine Zukunft mehr. Und in der Regel, wenn eine Methode die Möglichkeit hat, einen Fehler zu erzeugen, wird "Entweder/Option" verwendet. plus, ich bekomme nicht, warum der Rückgabetyp 'Future [String]' anstelle von 'Seq [Future [String]]' – suish

+0

ist Ich brauche das erste erfolgreiche Ergebnis, nicht alle Ergebnisse. Ich habe mehrere Server und nur einer von ihnen ist zu jedem Zeitpunkt aktiv. Inaktive Server antworten mit Fehler. Ich muss eine Zukunft zurückgeben, weil ich sie weitergeben muss, um {...} von akka-http zu vervollständigen. – kostya

+0

Ich bin mir nicht sicher, wie sehr Sie an diese Funktionsdefinitionen gebunden sind, aber "Future.find (...)" könnte einen Blick wert sein. Dies würde Ihnen erlauben, etwas wie "Future.find" (urls.map (u => Future (u))) (isSuccessfulRequest) zu tun, wobei "isSuccessfulRequest" als wahr ausgewertet würde, wenn die Anfrage erfolgreich war. – Brian

Antwort

2
def requestFirst(urls: List[String]): Future[String] = { 
    val default: Future[String] = Future.failed(new scala.Exception("all failed")) 
    urls.foldLeft(default)((prevFuture, currentUrl) => { 
    prevFuture fallbackTo (request(currentUrl)) 
    }) 
} 

ODER

def requestFirst(urls: List[String]): Future[String] = { 
    def requestFirstInternal(urlSubset: List[String]): Future[String] = { 
    if(urlSubset.isEmpty) { 
     Future.failed(new Exception("Exhausted all urlSubset")) 
    } else { 
     request(urlSubset.head) fallbackTo { 
     requestFirstInternal(urlSubset.tail) 
     } 
    } 
    } 
    requestFirstInternal(urls) 
} 
+2

Wahrscheinlich ist es besser, Kopf/Schwanz der Liste statt Magie mit Indizes – Nyavro

+0

zu bekommen Wie man Fehler Antworten sammeln? – kostya

+0

@ Nyavro, guter Vorschlag. Ich habe die Antwort aktualisiert –

0

Dies ist, was ich mit basierend auf @ kam parnav-Shukla beantworten:

def requestFirst(urls: List[String]): Future[String] = { 
    def requestFirstInternal(urlSubset: List[String], errors: Seq[String]): Future[String] = { 
    if(urlSubset.isEmpty) { 
     Future.failed(new Exception(errors.mkString("\n"))) 
    } else { 
     request(urlSubset.head) recoverWith { t => 
     requestFirstInternal(urlSubset.tail, errors ++ Seq(t.getMessage)) 
     } 
    } 
    } 
    requestFirstInternal(urls, Seq.empty) 
} 
Verwandte Themen