LoginSignup
2
6

More than 5 years have passed since last update.

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

Posted at

この記事では既存のライブラリなどでコールバックや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さわって数時間のビギナーなんで、もっといい方法があるのかも。
とりあえず動いた方法の紹介でした。

マサカリ歓迎。

2
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
6