What are the use cases of scala.concurrent.Promise?

15,843

The Promise and Future are complementary concepts. The Future is a value which will be retrieved, well, sometime in the future and you can do stuff with it when that event happens. It is, therefore, the read or out endpoint of a computation - it is something that you retrieve a value from.

A Promise is, by analogy, the writing side of the computation. You create a promise which is the place where you'll put the result of the computation and from that promise you get a future that will be used to read the result that was put into the promise. When you'll complete a Promise, either by failure or success, you will trigger all the behavior which was attached to the associated Future.

Regarding your first question, how can it be that for a promise p we have p.future == p. You can imagine this like a single-item buffer - a container which is initially empty and you can afterwords store one value which will become its content forever. Now, depending on your point of view this is both a Promise and a Future. It is promise for someone who intends to write the value in the buffer. It is a future for someone who waits for that value to be put in the buffer.

Specifically, for the Scala concurrent API, if you take a look at the Promise trait in here you can see how the methods from the Promise companion object are implemented :

object Promise {

  /** Creates a promise object which can be completed with a value.
   *  
   *  @tparam T       the type of the value in the promise
   *  @return         the newly created `Promise` object
   */
  def apply[T](): Promise[T] = new impl.Promise.DefaultPromise[T]()

  /** Creates an already completed Promise with the specified exception.
   *  
   *  @tparam T       the type of the value in the promise
   *  @return         the newly created `Promise` object
   */
  def failed[T](exception: Throwable): Promise[T] = new impl.Promise.KeptPromise[T](Failure(exception))

  /** Creates an already completed Promise with the specified result.
   *  
   *  @tparam T       the type of the value in the promise
   *  @return         the newly created `Promise` object
   */
  def successful[T](result: T): Promise[T] = new impl.Promise.KeptPromise[T](Success(result))

}

Now, those implementation of promises, DefaultPromise and KeptPromise can be found here. They both extend a base little trait which happens to have the same name, but it is located in a different package:

private[concurrent] trait Promise[T] extends scala.concurrent.Promise[T] with scala.concurrent.Future[T] {
  def future: this.type = this
}

So you can see what they mean by p.future == p.

DefaultPromise is the buffer I was referring above, while KeptPromise is a buffer with the value put in from its very creation.

Regarding your example, the future block you use there actually creates a promise behind the scenes. Let's look at the definition of future in here :

def future[T](body: =>T)(implicit execctx: ExecutionContext): Future[T] = Future[T](body)

By following the chain of methods you end up in the impl.Future:

private[concurrent] object Future {
  class PromiseCompletingRunnable[T](body: => T) extends Runnable {
    val promise = new Promise.DefaultPromise[T]()

    override def run() = {
      promise complete {
        try Success(body) catch { case NonFatal(e) => Failure(e) }
      }
    }
  }

  def apply[T](body: =>T)(implicit executor: ExecutionContext): scala.concurrent.Future[T] = {
    val runnable = new PromiseCompletingRunnable(body)
    executor.execute(runnable)
    runnable.promise.future
  }
}

So, as you can see, the result you get from your producer block gets poured into a promise.

LATER EDIT:

Regarding the real-world use: Most of the time you won't deal with promises directly. If you'll use a library which performs asynchronous computation then you'll just work with the futures returned by the library's methods. Promises are, in this case, created by the library - you're just working with the reading end of what those methods do.

But if you need to implement your own asynchronous API you'll have to start working with them. Suppose you need to implement an async HTTP client on top of, lets say, Netty. Then your code will look somewhat like this

    def makeHTTPCall(request: Request): Future[Response] = {
        val p = Promise[Response]
        registerOnCompleteCallback(buffer => {
            val response = makeResponse(buffer)
            p success response
        })
        p.future
    }
Share:
15,843
xiefei
Author by

xiefei

Updated on July 17, 2022

Comments

  • xiefei
    xiefei almost 2 years

    I am reading SIP-14 and the concept of Future makes perfect sense and easy to understand. But have two questions about Promise:

    1. The SIP says Depending on the implementation, it may be the case that p.future == p. How can this be? Are Future and Promise not two different types?

    2. When should we use a Promise? The example producer and consumer code :

      import scala.concurrent.{ future, promise }
      val p = promise[T]
      val f = p.future
      
      val producer = future {
          val r = produceSomething()
          p success r
          continueDoingSomethingUnrelated()
      }
      val consumer = future {
          startDoingSomething()
          f onSuccess {
              case r => doSomethingWithResult()
          }
      }
      

    is easy to read but do we really need to write like that? I tried to implement it only with Future and without Promise like this:

    val f = future {
       produceSomething()
    }
    
    val producer = future {
       continueDoingSomethingUnrelated()
    }
    
    startDoingSomething()
    
    val consumer = future {
      f onSuccess {
        case r => doSomethingWithResult()
      }
    }
    

    What is the difference between this and the given example and what makes a Promise necessary?

  • xiefei
    xiefei over 11 years
    I have to say the code style that use the same class name in different places representing different things makes me much more confused than before :( And still don't get the neccessity of promise and cannot imagine a real-life use case of promise :(
  • Dylan
    Dylan over 11 years
    @xiefei The use case for Promises should be in implementation code. Future is a nice, read-only thing that you can expose to client code. Also, the Future.future{...} syntax can be cumbersome sometimes.
  • Marius Danila
    Marius Danila over 11 years
    You can see it like this: you can't have a future without a promise. A future cannot return a value if there is no promise that gets completed in the first place. Promises are not optional, they are the mandatory writing side of a future. You cannot work just with futures because there wouldn't be anyone to provide them the return value.
  • Marius Danila
    Marius Danila over 11 years
    I think I see what you mean by real world uses: I've updated my response to give you an example.
  • LaloInDublin
    LaloInDublin about 10 years
    Very helpful example.. using a promise over the generic future method didn't seem to make sense, but sending the promise via a callback method suddenly makes it clear.
  • Cpt. Senkfuss
    Cpt. Senkfuss almost 8 years
    I guess it would've been less confusing had they given both, the read and write side, the same name. Promise or Future, either is fine. It's having the two next to each other that's problematic
  • puneetk
    puneetk about 7 years
    @Marius : Considering the real-world example given, what if makeHTTPCall is implemented like this: def makeHTTPCall(request: Request): Future[Response] = { Future { registerOnCompleteCallback(buffer => { val response = makeResponse(buffer) response }) } }
  • Evgeny Veretennikov
    Evgeny Veretennikov over 6 years
    @puneetk then you'll have future, which completes right after registerOnCompleteCallback() finished. Also, it doesn't return Future[Response]. It returns Future[registerOnCompleteCallback() return type] instead.