Czytam SIP-14 i koncepcja Future
ma doskonały sens i jest łatwa do zrozumienia. Ale mam dwa pytania dotyczące Promise
:
SIP mówi
Depending on the implementation, it may be the case that p.future == p
. Jak to może być? Czy sąFuture
iPromise
nie są dwoma różnymi typami?Kiedy powinniśmy użyć
Promise
? Przykładowyproducer and consumer
kod: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() } }
jest łatwy do odczytania, ale czy naprawdę musimy tak pisać? Próbowałem to zaimplementować tylko z Future i bez Promise w ten sposób:
val f = future {
produceSomething()
}
val producer = future {
continueDoingSomethingUnrelated()
}
startDoingSomething()
val consumer = future {
f onSuccess {
case r => doSomethingWithResult()
}
}
Jaka jest różnica między tym a podanym przykładem i co sprawia, że obietnica jest konieczna?
scala
concurrency
xiefei
źródło
źródło
Future
iPromise
są to dwa oddzielne typy, ale jak widać na stronie github.com/scala/scala/blob/master/src/library/scala/concurrent/ ... ta konkretnaPromise
implementacja również się rozszerzaFuture
.Odpowiedzi:
Obietnica i przyszłość to koncepcje uzupełniające się. Przyszłość to wartość, która zostanie odzyskana, no cóż, kiedyś w przyszłości i możesz z nią coś zrobić, gdy nastąpi to wydarzenie. Jest to zatem punkt końcowy odczytu lub wyjścia obliczenia - jest to coś, z czego pobierasz wartość.
Obietnica jest analogicznie stroną obliczeń do pisania. Tworzysz obietnicę, która jest miejscem, w którym umieścisz wynik obliczeń i z tej obietnicy otrzymasz przyszłość, która zostanie wykorzystana do odczytania wyniku, który został umieszczony w obietnicy. Kiedy wypełnisz obietnicę, albo przez porażkę, albo sukces, wywołasz wszystkie zachowania, które były związane z powiązaną z nią Przyszłością.
Odnosząc się do twojego pierwszego pytania, jak to możliwe, że mamy obietnicę p
p.future == p
. Możesz to sobie wyobrazić jako bufor pojedynczego elementu - kontener, który jest początkowo pusty i możesz później przechowywać jedną wartość, która na zawsze stanie się jego zawartością. Teraz, w zależności od twojego punktu widzenia, jest to zarówno obietnica, jak i przyszłość. To obietnica dla kogoś, kto zamierza zapisać wartość w buforze. To przyszłość dla kogoś, kto czeka, aż ta wartość zostanie umieszczona w buforze.W szczególności w przypadku współbieżnego interfejsu API Scala, jeśli spojrzysz na cechę Promise w tym miejscu , możesz zobaczyć, jak są implementowane metody z obiektu towarzyszącego Promise:
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)) }
Teraz te implementacje obietnic, DefaultPromise i KeptPromise można znaleźć tutaj . Oba rozszerzają podstawową małą cechę, która ma tę samą nazwę, ale znajduje się w innym opakowaniu:
private[concurrent] trait Promise[T] extends scala.concurrent.Promise[T] with scala.concurrent.Future[T] { def future: this.type = this }
Możesz więc zobaczyć, co mają na myśli
p.future == p
.DefaultPromise
jest buforem, o którym mówiłem powyżej, podczas gdyKeptPromise
jest buforem z wartością wprowadzoną od samego jego utworzenia.Jeśli chodzi o twój przykład, przyszły blok, którego tam używasz, faktycznie tworzy obietnicę za kulisami. Spójrzmy na definicję
future
w tutaj :def future[T](body: =>T)(implicit execctx: ExecutionContext): Future[T] = Future[T](body)
Postępując zgodnie z łańcuchem metod, znajdziesz się w 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 } }
Tak więc, jak widać, wynik uzyskany z bloku producenta zamienia się w obietnicę.
PÓŹNIEJSZA EDYCJA :
Odnośnie zastosowania w świecie rzeczywistym: w większości przypadków nie będziesz zajmować się bezpośrednio obietnicami. Jeśli użyjesz biblioteki, która wykonuje obliczenia asynchroniczne, będziesz po prostu pracować z futures zwracanymi przez metody biblioteki. W tym przypadku obietnice są tworzone przez bibliotekę - po prostu pracujesz nad czytaniem tego, co robią te metody.
Ale jeśli chcesz zaimplementować własne asynchroniczne API, musisz zacząć z nimi pracować. Załóżmy, że musisz zaimplementować asynchronicznego klienta HTTP oprócz, powiedzmy, Netty. Wtedy twój kod będzie wyglądał mniej więcej tak
def makeHTTPCall(request: Request): Future[Response] = { val p = Promise[Response] registerOnCompleteCallback(buffer => { val response = makeResponse(buffer) p success response }) p.future }
źródło
Promise
s powinien znajdować się w kodzie implementacji.Future
to przyjemna, tylko do odczytu rzecz, którą można ujawnić kodowi klienta. PonadtoFuture.future{...}
składnia może być czasami kłopotliwa.def makeHTTPCall(request: Request): Future[Response] = { Future { registerOnCompleteCallback(buffer => { val response = makeResponse(buffer) response }) } }
registerOnCompleteCallback()
zakończeniu. Poza tym nie wracaFuture[Response]
.Future[registerOnCompleteCallback() return type]
Zamiast tego wraca .