Ожидание результата от нескольких обратных вызовов / лямбд в Kotlin - PullRequest
0 голосов
/ 05 марта 2020

Я делаю приложение в Kotlin. До этого момента мои сетевые вызовы не должны были использоваться вместе. Сейчас я нахожусь в месте, где мне нужно сделать два одновременных сетевых вызова, сделать паузу, пока я не получу оба их ответа, а затем продолжить выполнение. Я пытаюсь выполнить sh что-то вроде этого:

    //first networking call, get resourceOne
    var resourceOne : String?
    Server.asyncRequest(RequestBuilder(endpoints.second, ids, params)) { resource: String?, error: ServiceError? ->
        resourceOne = resource
    }

    //second networking call, get resourceTwo
    var resourceTwo : String?
    Server.asyncRequest(RequestBuilder(endpoints.third, ids, params)) { resource: String?, error: ServiceError? ->
        resourceTwo = resource
    }

    //do something here wiith resourceOne and resourceTwo

Заголовок функции для моей функции asyncRequest:

fun asyncRequest(requestBuilder: RequestBuilder, completion: (resource: String?, error: ServiceError?) -> Unit) {

Он просто оборачивает запрос okhttp и делает некоторые дополнительная обработка / разбор. Обычно я просто беру результат (ресурс) и обрабатываю его внутри лямбда-завершения, но так как мне нужны оба значения, я не могу сделать это здесь. Я пытался сделать что-то похожее на this , но моя функция asyncRequest не имеет возвращаемого типа, поэтому я не могу выполнить async / await, как это делает ссылка.

1 Ответ

1 голос
/ 06 марта 2020

Вы можете сделать это с помощью Сопрограммы вместе с Потоком примерно так:

Превратите обратных вызовов в приостановленных функций с блоком suspendCancellableCoroutine {...}:

suspend fun <T> request(requestBuilder: RequestBuilder): T = suspendCancellableCoroutine { cont ->
    Server.asyncRequest(requestBuilder) { resource: T, error: ServiceError? ->
        if(error != null)
            cont.resumeWithException(error) // Makes the Flow throw an exception
        else
            cont.resume(resource) // Makes the Flow emit a correct result
    }
}

Создание Flow для выполнения первого запроса :

val resourceOneFlow = flow {
    emit(request<String>(RequestBuilder(endpoints.second, ids, params)))
}

Создание Поток для выполнения второго запроса :

val resourceTwoFlow = flow {
    emit(request<String>(RequestBuilder(endpoints.third, ids, params)))
}

Объединение обоих Потоки с оператором zip:

val requestsResultFlow = resourceOneFlow.zip(resourceTwoFlow) { resourceOne, resourceTwo ->
    // Build whatever you need with resourceOne and resourceTwo here and let it flow
    "$resourceOne $resourceTwo".length // Here I concatenate both strings and return its length
}

Активируйте / запустите Flow с оператором collect и используйте его результат :

requestsResultFlow.collect { length ->
    // Consume the result here
    println("$length") // Here I print the number received
}

У вас есть Поток Документация Здесь .

...