coroutineとコールバック形式の非同期関数を連携する

この記事では既存のライブラリなどでコールバックやPromiseを使っている非同期関数が既にある場合に、awaitで結果を渡す方法について説明します。

async/awaitの説明で良くあるコードはこんな感じです。

    fun async1() = async(CommonPool) {
        delay(1000)
        return@async 1
    }

    fun async2() = async(CommonPool) {
        delay(1000)
        return@async 2
    }

    fun testAsync() = launch(UI) {
        val result1 = async1().await()
        info { "result1: $result1" }
        val result2 = async2().await()
        info { "result2: $result2" }
    }

このコードを実行すると期待通り、実行して1秒後にresult1: 1が表示され、さらに1秒後にresult2: 2が表示されます。
なんの問題もありません。

で、今回はfirestoreからデータ取得する部分でawait使えるようにしたかったので、こんな感じの関数を作りました。
return@async 1してる部分を,firestoreから取得したデータのインスタンスを返すイメージです。

    fun async1() = async(CommonPool) {
        firestore.document("/employees/1").get().addOnSuccessListener { documentSnapshot ->
            val employee = documentSnapshot.toObject(Employee::class.java)
            return@async employee
        }.addOnFailureListener {
            error { "fetch employee failed" }
            return@async null
        }
    }

すると return@async employee'return' is not allowed here と怒られます。
確かにLambda式の中だから直接asyncのreturnができないのか。
でもどうすればいいのかわからずググっていたところ、suspendCoroutine なるキーワードを見つけました。

次のように使用します。期待する戻り値の型(Employee)をgenericsで指定して、cont.resume(employee)で結果を返します。Promiseのresolveやrxのobserver.nextのようなイメージですね。

    fun async1() = async(CommonPool) {
        val employee = suspendCoroutine<Employee> { cont ->
            firestore.document("/employees/1").get().addOnSuccessListener { documentSnapshot ->
                val employee = documentSnapshot.toObject(Employee::class.java)
                cont.resume(employee)
            }.addOnFailureListener {
                error { "fetch employee failed" }
                cont.resumeWithException(it)
            }
        }
        return@async employee
    }

呼び出し側はこんな感じ。

    fun testAsync() = launch(UI) {
        val employee = async1().await()
        info { "name: ${employee.name}" }
    }

Coroutinさわって数時間のビギナーなんで、もっといい方法があるのかも。
とりあえず動いた方法の紹介でした。

マサカリ歓迎。

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.