Upgrade to Pro — share decks privately, control downloads, hide ads and more …

async/awaitで快適非同期ライフ

 async/awaitで快適非同期ライフ

2017/04/04に行われた第5回Kotlin勉強会@Sansanの発表資料です。

Keita Kagurazaka

April 04, 2017
Tweet

More Decks by Keita Kagurazaka

Other Decks in Programming

Transcript

  1. アプローチ1: 完了時コールバック fun doSomething(completed: () -> Unit) { // 非同期処理

    completed() } fun main(args: Array<String>) { doSomething { println(“Done!”) } // ... }
  2. タスクA→タスクB→タスクC→タスクD (直列) fun main(args: Array<String>) { taskA { taskB {

    taskC { taskD { println(“Done!”) } } } } // ... } コールバック地獄
  3. アプローチ2: ReactiveX (or Promise) fun main(args: Array<String>) { taskA().andThen(taskB) .andThen(taskC)

    .andThen(taskD) .subscribe { println(“Done!”) } // ... } 慣れれば読める
  4. async/await fun main(args: Array<String>) { async(CommonPool) { taskA().await() taskB().await() taskC().await()

    taskD().await() println(“Done!”) } // ... } async/awaitを除くと 同期的な書き方と同じ!
  5. コルーチン • 途中で中断・再開できるサブルーチン(関数)の一般化 • 中断するので呼び出し元スレッドをブロックしない fun main(args: Array<String>) { async(CommonPool)

    { // コルーチン作成 taskA().await() // awaitでコルーチンを中断、完了したらここから再開 delay(1000) // コルーチンを中断、1000ms後にここから再開 taskB().await() // awaitでコルーチンを中断、完了したらここから再開 println(“Done!”) } // ... }
  6. よく使うコルーチンビルダー ビルダー launch async<T> runBlocking<T> 戻り値 Job Deferred<T> T 使い方

    val job = launch(context) { // 処理 } job.join() val deferred = launch(context) { // T型を返す処理 } val result = deferred.await() fun main(args: Array<String>) = runBlocking<Unit> { // 処理 } 説明 結果を返さないコルーチンを作 成するビルダー。 Jobをjoinすると完了まで中断、 cancelでキャンセルできる。 T型の結果を返すコルーチンを作 成するビルダー。 Deferred<T>をawaitすると完了 まで中断、cancelでキャンセルで きる。 呼び出しスレッドをブロックする コルーチンを作成するビル ダー。 main関数やテストメソッドなどが 主な活躍場所。
  7. よく使うコルーチンビルダー ビルダー launch async<T> runBlocking<T> 戻り値 Job Deferred<T> T 使い方

    val job = launch(context) { // 処理 } job.join() val deferred = launch(context) { // T型を返す処理 } val result = deferred.await() fun main(args: Array<String>) = runBlocking<Unit> { // 処理 } 説明 結果を返さないコルーチンを作 成するビルダー。 Jobをjoinすると完了まで中断、 cancelでキャンセルできる。 T型の結果を返すコルーチンを作 成するビルダー。 Deferred<T>をawaitすると完了 まで中断、cancelでキャンセルで きる。 呼び出しスレッドをブロックする コルーチンを作成するビル ダー。 main関数やテストメソッドなどが 主な活躍場所。
  8. よく使うコルーチンコンテキスト • CommonPool ◦ コルーチンをスレッドプール上で実行するdispatcher ◦ ForkJoinPoolがあればそのcommonPoolを使う ◦ なければExecutors.newFixedThreadPoolをコア数-1スレッドで作成 ◦

    スレッド数指定はできません • UI系 ◦ 各プラットフォームのUIスレッド上でコルーチンを実行するdispatcher ▪ UI (Android) ▪ JavaFx (JavaFx) ▪ Swing (Swing)
  9. コード例1: 非同期処理をmainで呼ぶ fun asyncDoHeavyWork() = async(CommonPool) { // 処理してIntを返す }

    fun main(args: Array<String>) = runBlocking<Unit> { val result = asyncDoHeavyWork().await() println(“Result: ${result * 2}”) }
  10. コード例2: カウントダウン fun onStartCountDownButtonClick() = launch(UI) { for (count in

    10 downTo 1) { countDownTextView.text = “Count: $count” delay(1000) } countDownTextView.text = “Start!” }
  11. Rx連携 fun asyncDoHeavyWork(): Single<Int> = Single.create { // 処理 it.onSuccess(result)

    }.subscribeOn(Schedulers.io()) fun main(args: Array<String>) = runBlocking<Unit> { val result = asyncDoHeavyWork().await() println(“Result: ${result * 2}”) }
  12. Rx連携 fun asyncDoHeavyWork(): Single<Int> = rxSingle(CommonPool) { // 処理 return@rxSingle

    result } fun main(args: Array<String>) = runBlocking<Unit> { val result = asyncDoHeavyWork().await() println(“Result: ${result * 2}”) }
  13. Rxの場合 val streamA = taskA().toObservable().share() // hot Observable化 val streamB

    = taskB().toObservable() val streamC = streamA.zipWith(streamB) { a, b -> taskC(a, b) } .flatMap { it.toObservable() } val streamD = streamA.flatMap { task.D(it).toObservable() } val streamE = streamC.zipWith(streamD) { c, d -> taskE(c, d) } .flatMap { it.toObservable() } streamE.subscribe { val result = it } • taskAの結果が2箇所で使われるのでhot Observableにする必要がある • operatorの関係で全体的にSingle -> Observableにしなければいけない • 見た目がグロい
  14. async/awaitの場合 fun taskD(aDeferred: Deferred<Int>) = async(CommonPool) { // 処理 val

    a = aDeferred.await() // aが必要になったタイミングでawait // aが必要な処理 return@async result }
  15. async/awaitの場合 fun taskD(aDeferred: Deferred<Int>) = async(CommonPool) { // 処理 val

    a = aDeferred.await() // aが必要になったタイミングでawait // aが必要な処理 return@async result } val a = taskA() val b = taskB() val c = taskC(a, b) val d = taskD(a) val e = taskE(c, d) val result = e.await()
  16. async/awaitの場合 fun taskD(aDeferred: Deferred<Int>) = async(CommonPool) { // 処理 val

    a = aDeferred.await() // aが必要になったタイミングでawait // aが必要な処理 return@async result } val a = taskA() val b = taskB() val c = taskC(a, b) val d = taskD(a) val e = taskE(c, d) val result = e.await() ほとんど同期と変わらない!!!