Создание сопрограммы Kotlin Отложенный объект, который генерирует результаты обратного вызова слушателя - PullRequest
0 голосов
/ 13 октября 2018

В настоящее время я переключаюсь с сопрограмм RxJava на Kotlin в проекте, заменяя все типы возвращаемых данных Single и Observable на сопрограммы сопрограмм.Я все еще борюсь со следующей конструкцией: Интерфейс (например, хранилище) предлагает доступ к данным и возвращает RxJava Single.Реализация создает объект Single с Single.create и выдает результат с помощью onSuccess / onError.Теперь, что нужно сделать реализации для извлечения данных, это создать прослушиватель с обратными вызовами и зарегистрировать его.Затем обратные вызовы этого созданного прослушивателя будут вызывать onSuccess / onError для самодельного Single.Например, используя firebase (хотя мой вопрос не относится к firebase):

interface Repository {
    fun getData(query: Query): Single<DataSnapshot?>
}

fun getData(query: Query): Single<DataSnapshot?> = Single.create { emitter ->
    query.addListenerForSingleValueEvent(object : ValueEventListener {
        override fun onCancelled(error: DatabaseError?) {
            emitter.onError(Exception())
        }

        override fun onDataChange(data: DataSnapshot?) {
            emitter.onSuccess(data)
        }
    })
}

Теперь я хочу иметь интерфейсный метод, возвращающий сопрограмму Deferred.Как нужно создать реализацию, чтобы также можно было зарегистрировать слушателя с обратными вызовами, результаты которого затем будут доставлены Отложенным?Я не вижу способа, чтобы эти компиляторы, такие как async, launch и т. Д., Делали то, что делал бы onSuccess / onError.

interface Repository {
    fun getData(query: Query): Deferred<DataSnapshot?>
}

Ответы [ 2 ]

0 голосов
/ 14 октября 2018

Я думаю, что самая близкая вещь из того, что я ищу, была бы ReceiveChannel.Придумали это решение:

override fun getData(query: Query): ReceiveChannel<Datasnapshot?> = GlobalScope.produce {
    query.addListenerForSingleValueEvent(object : ValueEventListener {
        override fun onDataChange(data: DataSnapshot?) {
            launch { send(data) } }
        }

        override fun onCancelled(error: DatabaseError?) {
            throw Exception()
        }
    })
}

Не уверен, что это излишнее количество, и будут лучшие варианты, предложения приветствуются

0 голосов
/ 14 октября 2018

Мое предложение выглядит следующим образом:

interface Repository {
    suspend fun getData(query: Query): Result<DataSnapshot>
}

, где Result может быть запечатанным классом со случаями Success и Error:

sealed class Result<T> {
    class Success<T>(result: T) : Result<T>()
    class Error<T>(error: String) : Result<T>()
}

Таким образом, на стороне реализации getData выможет делать:

return Success(yourData)

или

return Error("Something went wrong")

В общем, при работе с сопрограммами вы должны избегать возврата отложенных значений и пытаться использовать их «как синхронные методы».

Edit: теперь, когда я понимаю проблему, я надеюсь, что это поможет решить ее:

//This is as generic as it gets, you could use it on any Query, no need to retype it
suspend fun Query.await(): DataSnapshot = suspendCoroutine{cont ->
    addListenerForSingleValueEvent(object : ValueEventListener{
        override fun onCancelled(error: DatabaseError?) {
            cont.resumeWithException(error?: Exception("Unknown Error"))
        }

        override fun onDataChange(data: DataSnapshot?) {
            if(data != null){
                cont.resume(data)
            } else {
                cont.resumeWithException(Exception("Null data"))
            }

        }
    })
}
//this is your actual implementation
suspend fun getData(query: Query):DataSnapshot =
        query.await()

Этот код предполагает, что DatabaseError расширяет Exception или Throwable.Если нет, вам нужно создать для него тип оболочки или использовать мое оригинальное решение и использовать регулярное резюме в обоих случаях.

...