저는 SIP-14를 읽고 있으며 개념 Future
이 완벽하고 이해하기 쉽습니다. 그러나 다음에 대해 두 가지 질문이 있습니다 Promise
.
-
SIP는 말한다
Depending on the implementation, it may be the case that p.future == p
. 어떻게 이럴 수있어? 인가Future
와Promise
서로 다른 두 가지 종류가 있지? -
우리는 언제 사용해야
Promise
합니까? 예제producer and consumer
코드 :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() } }
읽기 쉽지만 그렇게 써야하나요? 나는 다음과 같이 미래와 약속없이 그것을 구현하려고했습니다.
val f = future {
produceSomething()
}
val producer = future {
continueDoingSomethingUnrelated()
}
startDoingSomething()
val consumer = future {
f onSuccess {
case r => doSomethingWithResult()
}
}
이것과 주어진 예의 차이점은 무엇이며 약속이 필요한 이유는 무엇입니까?
답변
약속과 미래는 상호 보완적인 개념입니다. 미래는 언젠가 검색 될 가치이며 그 사건이 발생했을 때 그것을 가지고 무언가를 할 수 있습니다. 따라서 이는 계산의 읽기 또는 출력 끝점이며 값을 검색하는 것입니다.
Promise는 유사하게 계산의 작성 측면입니다. 당신은 계산의 결과를 넣을 장소 인 promise를 만들고 그 약속으로부터 약속에 넣은 결과를 읽는 데 사용될 미래를 얻습니다. 실패 또는 성공으로 Promise를 완료하면 연결된 Future에 연결된 모든 동작이 트리거됩니다.
첫 번째 질문과 관련하여 우리가 가진 약속 p에 대해 어떻게 될 수 있습니까 p.future == p
? 이것은 단일 항목 버퍼 (처음에 비어있는 컨테이너)와 같이 상상할 수 있으며, 나중에 내용이 영원히 내용이 될 하나의 값을 저장할 수 있습니다. 이제 여러분의 관점에 따라 이것은 약속이자 미래입니다. 버퍼에 값을 쓰려는 사람에게는 약속입니다. 그 값이 버퍼에 들어가기를 기다리는 사람에게는 미래입니다.
특히 Scala 동시 API의 경우 여기 에서 Promise 특성을 살펴보면 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))
}
이제 이러한 promise 구현, DefaultPromise 및 KeptPromise는 여기 에서 찾을 수 있습니다 . 둘 다 동일한 이름을 가진 기본 작은 특성을 확장하지만 다른 패키지에 있습니다.
private[concurrent] trait Promise[T] extends scala.concurrent.Promise[T] with scala.concurrent.Future[T] {
def future: this.type = this
}
그래서 당신은 그들이 의미하는 바를 볼 수 있습니다 p.future == p
.
DefaultPromise
는 위에서 언급 한 KeptPromise
버퍼이고, 생성 된 값을 가진 버퍼입니다.
귀하의 예와 관련하여 거기에서 사용하는 미래 블록은 실제로 장면 뒤에서 약속을 만듭니다. 의 정의에서 살펴 보자 future
에 여기 :
def future[T](body: =>T)(implicit execctx: ExecutionContext): Future[T] = Future[T](body)
일련의 메소드를 따르면 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
}
}
보시다시피 생산자 블록에서 얻은 결과가 약속에 부어집니다.
나중에 편집 :
실제 사용과 관련하여 : 대부분의 경우 약속을 직접 처리하지 않습니다. 비동기 계산을 수행하는 라이브러리를 사용하는 경우 라이브러리의 메서드에서 반환 된 Future로 작업 할 것입니다. 이 경우 약속은 라이브러리에 의해 생성됩니다. 이러한 메서드가 수행하는 작업의 읽기 끝으로 작업하고 있습니다.
그러나 자체 비동기 API를 구현해야하는 경우 작업을 시작해야합니다. 예를 들어 Netty 위에 비동기 HTTP 클라이언트를 구현해야한다고 가정합니다. 그러면 코드가 다음과 같이 보일 것입니다.
def makeHTTPCall(request: Request): Future[Response] = {
val p = Promise[Response]
registerOnCompleteCallback(buffer => {
val response = makeResponse(buffer)
p success response
})
p.future
}