La promesse et l'avenir sont des concepts complémentaires. L'avenir est une valeur qui sera récupérée, enfin, dans le futur et vous pouvez en faire des choses lorsque cet événement se produit. C'est donc le point final de lecture ou de sortie d'un calcul - c'est quelque chose à partir duquel vous récupérez une valeur.
Une promesse est, par analogie, le côté écriture du calcul. Vous créez une promesse qui est l'endroit où vous placerez le résultat du calcul et à partir de cette promesse, vous obtenez un avenir qui sera utilisé pour lire le résultat qui a été mis dans la promesse. Lorsque vous accomplirez une promesse, que ce soit par échec ou par succès, vous déclencherez tout le comportement qui était attaché au futur associé.
Concernant votre première question, comment se fait-il que pour une promesse p que nous ayons p.future == p
. Vous pouvez imaginer cela comme un tampon à un seul élément - un conteneur initialement vide et vous pouvez ensuite stocker une valeur qui deviendra son contenu pour toujours. Maintenant, selon votre point de vue, c'est à la fois une promesse et un avenir. C'est prometteur pour quelqu'un qui a l'intention d'écrire la valeur dans le tampon. C'est un avenir pour quelqu'un qui attend que cette valeur soit mise dans le tampon.
Plus précisément, pour l'API simultanée Scala, si vous examinez le trait Promise ici, vous pouvez voir comment les méthodes de l'objet compagnon Promise sont implémentées:
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))
}
Maintenant, ces implémentations de promesses, DefaultPromise et KeptPromise peuvent être trouvées ici . Ils étendent tous les deux un petit trait de base qui porte le même nom, mais il est situé dans un package différent:
private[concurrent] trait Promise[T] extends scala.concurrent.Promise[T] with scala.concurrent.Future[T] {
def future: this.type = this
}
Vous pouvez donc voir ce qu'ils veulent dire p.future == p
.
DefaultPromise
est le tampon KeptPromise
auquel je faisais référence ci-dessus, tandis que c'est un tampon avec la valeur mise depuis sa création même.
En ce qui concerne votre exemple, le futur bloc que vous y utiliserez crée en fait une promesse dans les coulisses. Le regard Let la définition de future
dans ici :
def future[T](body: =>T)(implicit execctx: ExecutionContext): Future[T] = Future[T](body)
En suivant la chaîne de méthodes, vous vous retrouvez dans le futur impl .
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
}
}
Ainsi, comme vous pouvez le voir, le résultat que vous obtenez de votre bloc de producteurs est versé dans une promesse.
MODIFICATION PLUS TARD :
En ce qui concerne l'utilisation dans le monde réel: la plupart du temps, vous ne traitez pas directement les promesses. Si vous utilisez une bibliothèque qui effectue un calcul asynchrone, vous ne travaillerez qu'avec les futurs retournés par les méthodes de la bibliothèque. Les promesses sont, dans ce cas, créées par la bibliothèque - vous travaillez simplement avec la fin de lecture de ce que font ces méthodes.
Mais si vous devez implémenter votre propre API asynchrone, vous devrez commencer à travailler avec elles. Supposons que vous ayez besoin d'implémenter un client HTTP asynchrone au-dessus de, disons, Netty. Ensuite, votre code ressemblera un peu à ceci
def makeHTTPCall(request: Request): Future[Response] = {
val p = Promise[Response]
registerOnCompleteCallback(buffer => {
val response = makeResponse(buffer)
p success response
})
p.future
}