Смущает выполнение Kotlin Coroutines: где вызывать await ()? - PullRequest
2 голосов
/ 29 мая 2019

Я прочитал много документов о сопрограммах Kotlin, но все еще сомневаюсь. Я использую Retrofit с сопрограммами, поэтому мне нужно сделать запрос с контекстом Dispatchers.IO, но использовать результат в контексте Dispatchers.Main, чтобы назначить его ViewModel. Мой код:

fun doHttpreq() {
    viewModelScope.launch(Dispatchers.IO) {
        try {
            //should I call await() here? (I guess the correct way to keep execution of request outside of Main thread)
            val request = RestClient.instance.getItems().await()

            withContext(Dispatchers.Main) {
                //or should I call await() here? (BUT need request to be executed outside of Main thread!)

                if (request.isSuccessful) {
                   //asign items to ViewModel

                } else {
                    //asign error to ViewModel
                }
            }

        } catch (e: Exception) {
            withContext(Dispatchers.Main) {
                //asign error to ViewModel
            }
        }
    }
}

Ответы [ 2 ]

1 голос
/ 29 мая 2019

Поскольку сопрограммы приостанавливаются, а не блокируются, не должно быть необходимости управлять потоком, в котором они работают.В вашем случае Retrofit справится с этим для вас.Также тип Deferred на самом деле является горячим источником данных.Это означает, что Call выполняется еще до того, как вы назовете await.await просто ждет данных, которые будут там.

Таким образом, вместо этого вы можете запустить диспетчер Main напрямую.Поэтому у вас есть только одно место для звонка await() из.

viewModelScope.launch(Dispatchers.Main) {
    try {
        val request = RestClient.instance.getItems().await()

        if (request.isSuccessful) {
            //asign items to ViewModel
        } else {
            //asign error to ViewModel
        }
    } catch (e: Exception) {
        //asign error to ViewModel
    }
}
1 голос
/ 29 мая 2019

Вы можете взять свою отложенную работу в переменной и затем ждать ее на вашем Главном диспетчере , как показано ниже:

try {
        //Rather than await here, you take your Job as Deffered
        val request: Deferred? = RestClient.instance.getItems()

        withContext(Dispatchers.Main) {
            //Yes, you can await here because it's non-blocking call and can be safely obtained from here once completed
            val result = request?.await()
            if (request.isSuccessful) {
               //asign items to ViewModel

            } else {
                //asign error to ViewModel
            }
        }

    } catch (e: Exception) {
        withContext(Dispatchers.Main) {
            //asign error to ViewModel
        }
    }

Какой официальный документ указывает на await():

Ожидает завершения этого значения без блокировки потока и возобновляет работу после завершения отложенного вычисления, возвращая результирующее значение или выдает соответствующее исключение , если отсроченное было отменено.

Эта функция приостановки отменяется.Если задание текущей сопрограммы отменяется или завершается во время ожидания этой функции приостановки, эта функция немедленно возобновляется с помощью CancellationException.

Эта функция может использоваться в вызове select с предложением onAwait.Используйте isCompleted для проверки завершения этого отложенного значения без ожидания.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...