กรณีการใช้งานของ scala.concurrent.Promise คืออะไร?


93

ฉันกำลังอ่านSIP-14และแนวคิดเรื่องFutureความสมเหตุสมผลและเข้าใจง่าย แต่มีคำถามสองข้อเกี่ยวกับPromise:

  1. SIP Depending on the implementation, it may be the case that p.future == pที่กล่าวว่า เป็นเช่นนี้ได้อย่างไร? มีFutureและPromiseไม่ได้ทั้งสองประเภทที่แตกต่างกัน?

  2. เราควรใช้ a 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()
  }
}

อะไรคือความแตกต่างระหว่างสิ่งนี้กับตัวอย่างที่ให้มาและสิ่งที่ทำให้คำสัญญาจำเป็น?


ในตัวอย่างแรกต่อไป ContinueDoingSomethingUnrelated () จะประเมินหลังจาก produceSomething () ในเธรดเดียวกัน
senia

1
ที่จะตอบคำถาม # 1 ใช่FutureและPromiseสองประเภทแยกกัน แต่เท่าที่คุณสามารถดูจากgithub.com/scala/scala/blob/master/src/library/scala/concurrent/...นี้โดยเฉพาะPromiseการดำเนินการขยายFutureเช่นกัน
Dylan

คำตอบ:


118

คำสัญญาและอนาคตเป็นแนวคิดเสริม อนาคตคือคุณค่าที่จะถูกเรียกคืนบางครั้งในอนาคตและคุณสามารถทำสิ่งต่างๆได้เมื่อเหตุการณ์นั้นเกิดขึ้น ดังนั้นจึงเป็นจุดสิ้นสุดของการคำนวณหรืออ่านหรือออก - เป็นสิ่งที่คุณดึงค่าจาก

คำสัญญาคือการเปรียบเทียบด้านการเขียนของการคำนวณ คุณสร้างคำสัญญาซึ่งเป็นสถานที่ที่คุณจะใส่ผลลัพธ์ของการคำนวณและจากคำสัญญานั้นคุณจะได้รับอนาคตที่จะใช้ในการอ่านผลลัพธ์ที่ใส่ไว้ในสัญญา เมื่อคุณทำตามคำมั่นสัญญาไม่ว่าจะด้วยความล้มเหลวหรือความสำเร็จคุณจะกระตุ้นพฤติกรรมทั้งหมดที่แนบมากับอนาคตที่เกี่ยวข้อง

เกี่ยวกับคำถามแรกของคุณ, p.future == pวิธีที่จะสามารถเป็นไปได้ว่าสำหรับหน้าสัญญาที่เรามี คุณสามารถจินตนาการได้ว่าสิ่งนี้เป็นเหมือนบัฟเฟอร์รายการเดียว - คอนเทนเนอร์ซึ่งในตอนแรกว่างเปล่าและคุณสามารถเก็บค่าหลังคำหนึ่งซึ่งจะกลายเป็นเนื้อหาตลอดไป ตอนนี้ขึ้นอยู่กับมุมมองของคุณนี่เป็นทั้งคำสัญญาและอนาคต เป็นสัญญาสำหรับคนที่ตั้งใจจะเขียนค่าในบัฟเฟอร์ เป็นอนาคตสำหรับคนที่รอให้ค่านั้นถูกใส่ลงในบัฟเฟอร์

โดยเฉพาะอย่างยิ่งสำหรับ API ที่ทำงานร่วมกันของ Scala หากคุณดูลักษณะสัญญาในที่นี่คุณจะเห็นวิธีการใช้งานวิธีการจากวัตถุร่วมคำสัญญา:

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))

}

ตอนนี้การดำเนินการของผู้สัญญา 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เป็นบัฟเฟอร์ที่มีค่าที่นำมาจากการสร้างมาก

เกี่ยวกับตัวอย่างของคุณบล็อกในอนาคตที่คุณใช้ที่นั่นสร้างสัญญาเบื้องหลัง ดู Let 's ที่นิยามของ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
  }
}

ดังที่คุณเห็นผลลัพธ์ที่คุณได้รับจากบล็อกโปรดิวเซอร์ของคุณถูกเทลงในสัญญา

แก้ไขภายหลัง :

เกี่ยวกับการใช้งานในโลกแห่งความเป็นจริง: ส่วนใหญ่แล้วคุณจะไม่จัดการกับคำสัญญาโดยตรง หากคุณใช้ไลบรารีที่ทำการคำนวณแบบอะซิงโครนัสคุณก็จะทำงานกับฟิวเจอร์สที่ส่งคืนโดยวิธีการของไลบรารี ในกรณีนี้คำสัญญาเป็นสิ่งที่สร้างขึ้นโดยไลบรารี - คุณกำลังดำเนินการอ่านให้จบว่าวิธีการเหล่านั้นทำอะไร

แต่ถ้าคุณต้องการใช้ API แบบอะซิงโครนัสของคุณเองคุณจะต้องเริ่มทำงานกับ API เหล่านี้ สมมติว่าคุณต้องติดตั้งไคลเอนต์ async HTTP ที่ด้านบนสมมติว่า Netty จากนั้นรหัสของคุณจะมีลักษณะเช่นนี้

    def makeHTTPCall(request: Request): Future[Response] = {
        val p = Promise[Response]
        registerOnCompleteCallback(buffer => {
            val response = makeResponse(buffer)
            p success response
        })
        p.future
    }

3
@xiefei กรณีการใช้งานสำหรับPromises ควรอยู่ในรหัสการใช้งาน Futureเป็นสิ่งที่ดีอ่านอย่างเดียวที่คุณสามารถเปิดเผยกับรหัสไคลเอ็นต์ นอกจากนี้Future.future{...}บางครั้งไวยากรณ์อาจยุ่งยาก
Dylan

11
คุณจะเห็นแบบนี้คุณไม่สามารถมีอนาคตได้หากไม่มีคำสัญญา อนาคตไม่สามารถคืนค่าได้หากไม่มีสัญญาที่ทำให้สำเร็จตั้งแต่แรก คำสัญญาไม่ใช่ทางเลือก แต่เป็นการเขียนด้านบังคับของอนาคต คุณไม่สามารถทำงานกับฟิวเจอร์สได้เพราะจะไม่มีใครให้ผลตอบแทนแก่พวกเขา
Marius Danila

4
ฉันคิดว่าฉันเข้าใจว่าคุณหมายถึงอะไรเมื่อใช้ในโลกแห่งความเป็นจริงฉันได้อัปเดตคำตอบของฉันเพื่อให้เป็นตัวอย่าง
Marius Danila

2
@Marius: เมื่อพิจารณาจากตัวอย่างในโลกแห่งความเป็นจริงแล้วจะเกิดอะไรขึ้นถ้า makeHTTPCall ถูกนำมาใช้เช่นนี้: def makeHTTPCall(request: Request): Future[Response] = { Future { registerOnCompleteCallback(buffer => { val response = makeResponse(buffer) response }) } }
puneetk

1
@puneetk แล้วคุณจะมีอนาคตซึ่งจะเสร็จสมบูรณ์ทันทีหลังจากregisterOnCompleteCallback()เสร็จสิ้น ยังFuture[Response]ไงก็ไม่กลับมา มันกลับมาFuture[registerOnCompleteCallback() return type]แทน
Evgeny Veretennikov
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.