Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala Future - for comprehension, mix sync and async

In my method1, I need to call another method2 asynchronously, which returns Option (result1). Than, if result1 is empty, I need to call another method3 asynchronously, but if result1 is NOT empty, I just need to return it.

Here is the method:

  def signIn(username: String): Future[User] = {
    for {
      foundUser <- userService.findByUsername(username) // this method returns Future[Option[User]], 
      // foundUser is Option[User]
      user <- if (foundUser.isEmpty) {
        val newUser = User(username = "User123")
        userService.create(newUser).map(Some(_)) // this method returns Future[Option[User]]
      }
      else
        // Here I want to return just foundUser, of course, it is not possible. 
        // IS THIS APPROACH CORRECT?? DOES THIS LINE CREATE ASYNCHRONOUS CALL?          
        Future.successful(foundUser)
    } yield user 
  }

Question is:

Future.successful(foundUser) - is this approach correct in the code above? Does this line create asynchronous call? If so, how to avoid it? I've already fetched foundUser asynchronously, and I don't want to make additional async call just to return already fetched value.

like image 293
Teimuraz Avatar asked Aug 22 '16 20:08

Teimuraz


People also ask

Is Scala Future blocking?

By default, futures and promises are non-blocking, making use of callbacks instead of typical blocking operations. To simplify the use of callbacks both syntactically and conceptually, Scala provides combinators such as flatMap , foreach , and filter used to compose futures in a non-blocking way.

How do you handle Future Scala?

Handle the result of a future with methods like onComplete , or combinator methods like map , flatMap , filter , andThen , etc. The value in a Future is always an instance of one of the Try types: Success or Failure.

What are Scala promises?

Promise is an object which can be completed with a value or failed with an exception. A promise should always eventually be completed, whether for success or failure, in order to avoid unintended resource retention for any associated Futures' callbacks or transformations. Source Promise.scala. AnyRef, Any.

How do you unwrap the Future?

You can't unwrap the value of a Future because a Future represents the result of an asynchronous computation that may or may not be available yet. By default, futures and non-blocking, encouraging the use of callbacks instead of typical blocking operations.


1 Answers

Future.successful doesn't queue an additional function on the provided ExecutionContext. It simply uses a Promise[T] to create a completed Future[T]:

/** Creates an already completed Future with the specified result.
   *
   *  @tparam T       the type of the value in the future
   *  @param result   the given successful value
   *  @return         the newly created `Future` instance
   */
  def successful[T](result: T): Future[T] = Promise.successful(result).future

As a side note, you can reduce the amount of boilerplate using Option.fold:

def signIn(username: String): Future[User] = 
  userService
    .findByUsername(username)
    .flatMap(_.fold(userService.create(User(username = "User123")))(Future.successful(_))
like image 196
Yuval Itzchakov Avatar answered Oct 30 '22 01:10

Yuval Itzchakov