kostya
kostya

Reputation: 9559

How to execute an async function multiple times in sequence until the first success

Let's say I have a function

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

and need to write a function

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

that calls request(url) in sequence untill it completes successfully (in this case the successful value is returned) or requests for all urls fail (in this case all failures are returned).

How can this be done in Scala?

Upvotes: 2

Views: 203

Answers (2)

kostya
kostya

Reputation: 9559

This is what I came up with based on @parnav-shukla answer:

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)
}

Upvotes: 0

Pranav Shukla
Pranav Shukla

Reputation: 2226

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))
  })
}

OR

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)
}

Upvotes: 2

Related Questions