Choletski
Choletski

Reputation: 7515

Confusing about Kotlin Coroutines execution: where to call await()?

I read a lot of docs about Kotlin coroutines but still having some doubts. I'm using Retrofit with coroutines so I need to do request with Dispatchers.IO context but use result within Dispatchers.Main context to assign it to ViewModel. My code is:

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
            }
        }
    }
}

Upvotes: 2

Views: 401

Answers (2)

tynn
tynn

Reputation: 39853

As Coroutines are suspending instead of blocking, there should not be any need to manage the thread they are running on. In your case Retrofit handles this for you. Also the Deferred type is actually a hot data source. This means that the Call is executed before you even call await on it. await just waits for the data to be there.

So instead you can launch on the Main dispatcher directly. Therefore you only have one place to call await() from.

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
    }
}

Upvotes: 1

Jeel Vankhede
Jeel Vankhede

Reputation: 12118

You can take your deffered job in variable and then await it on your Main dispatcher like below :

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
        }
    }

What official doc states about await() :

Awaits for completion of this value without blocking a thread and resumes when deferred computation is complete, returning the resulting value or throwing the corresponding exception if the deferred was cancelled.

This suspending function is cancellable. If the Job of the current coroutine is cancelled or completed while this suspending function is waiting, this function immediately resumes with CancellationException.

This function can be used in select invocation with onAwait clause. Use isCompleted to check for completion of this deferred value without waiting.

Upvotes: 1

Related Questions