object Main extends App {
val p1 = Promise[Option[String]]()
val p2 = Promise[Option[String]]()
val f1 = p1.future
val f2 = p2.future
val res = (for{
file1Opt <- f1
file2Opt <- f2
file1 <- file1Opt
file2 <- file2Opt
} yield {
combineFiles(file1, file2)
}).fallbackTo(Future.successful("Files not found"))
Thread.sleep(2000)
println("XXXXXXXXXXXXXXXXXXX")
p1.success(Some("file one"))
p2.success(Some("file two"))
val finalData = res.map(s =>
s + " " + "add more data to the file"
)
finalData.map(println(_))
def combineFiles(f1: String, f2: String): String = {
f1 + " " + f2
}
}
I have two functions that return Future[Option[String]]
and I need to combine the two strings into one string.
I want the output to be either combination of two strings and footer: "file one file two add more data to the file" or default when one or both of the Future
s return None
: "Files not found add more data to file".
How can this be achieved?
Compiler error:
Error:(16, 11) type mismatch;
found : Option[String]
required: scala.concurrent.Future[?]
file1 <- file1Opt
^
The contract on Future#filter is thus: If the current future contains a value which satisfies the predicate, the new future will also hold that value. Otherwise, the resulting future will fail with a NoSuchElementException. But wait: scala> None.get java.util. NoSuchElementException: None.get.
Scala offers a lightweight notation for expressing sequence comprehensions. Comprehensions have the form for (enumerators) yield e , where enumerators refers to a semicolon-separated list of enumerators. An enumerator is either a generator which introduces new variables, or it is a filter.
Future. Future represents a result of an asynchronous computation that may or may not be available yet. When we create a new Future, Scala spawns a new thread and executes its code. Once the execution is finished, the result of the computation (value or exception) will be assigned to the Future.
The Promise is a writable, single-assignment container that completes a Future. The Promise is similar to the Future. However, the Future is about the read-side of an asynchronous operation, while the Promise is about the write-side.
Like alf mentioned in his answer, you can use monad tranformers for this, in this case OptionT
.
An example using cats :
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import cats.data.OptionT
import cats.implicits._
val file1: Future[Option[String]] = Future.successful(Some("file1"))
val file2: Future[Option[String]] = Future.successful(Some("file2"))
val combinedOT: OptionT[Future, String] =
for {
f1 <- OptionT(file1)
f2 <- OptionT(file2)
} yield s"$f1 $f2"
val combinedFO: Future[Option[String]] = combinedOT.value
val combinedF: Future[String] = combinedOT.getOrElse("Files not found")
Note that if you use cats, you can replace the for comprehension in combinedOT2
by using a cartesian builder (the |@|
), because file2
doesn't depend on file1
:
val combinedOT2: Future[Option[String]] =
(OptionT(file1) |@| OptionT(file2)).map(_ + " " + _).value
You can still use fallbackTo
if the "combined" Future
fails, eventhough it is probably better to use recover
or recoverWith
to actually check which Throwable
s you want to recover from.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With