简体   繁体   中英

Kotlin coroutine suspend and delay

I want to simulate file loading and I want to delay code for 4 seconds and I can't do this.

 suspend fun showLoadingProgress() : String = suspendCancellableCoroutine{ continuation ->

    while (fileIsBeingLoaded())
    {
        delay(4000)

        val percent = ((loadedBites.toDouble() / fileBites.toDouble())*100).toInt()

        continuation.resume("$loadedBites/$fileBites ($percent%)")
    }
}

I have error that: suspension functions can be called only from coroutine body. BUT

When I have code like this, without returning String, then my delay works.. WHY?:

   suspend fun showLoadingProgress() {

    while (fileIsBeingLoaded())
    {
        delay(4000)

        val percent = ((loadedBites.toDouble() / fileBites.toDouble())*100).toInt()

        continuation.resume("$loadedBites/$fileBites ($percent%)")
    }
}

How can I make delay and return a String?

suspendCancellableCoroutine is mainly used with callbacks to suspend a coroutine execution until the callback fires, for example:

suspend fun getUser(id: String): User = suspendCancellableCoroutine { continuation ->
          Api.getUser(id) { user ->
              continuation.resume(user)
          }
          continuation.invokeOnCancellation {
              // clear some resources, cancel tasks, close streams etc.
          }
    }

delay doesn't work in suspendCancellableCoroutine block because it is not marked as suspend and therefore we can't call suspend function in it. suspendCancellableCoroutine function is defined like:

public suspend inline fun <T> suspendCancellableCoroutine(
    crossinline block: (CancellableContinuation<T>) -> Unit
): T = ...

If it was defined something like this (please note block marked as suspend ):

public suspend inline fun <T> suspendCancellableCoroutine(
    crossinline block: suspend (CancellableContinuation<T>) -> Unit
): T = ...

then we would be able to call delay function in it.


I don't know why you use while loop, it seems it is redundant there. Or you use it incorrectly for the loading progress.

You don't have callbacks, so you can get rid of suspendCancellableCoroutine :

suspend fun getLoadingProgress(): String {
    delay(4000)
    val percent = ((loadedBites.toDouble() / fileBites.toDouble())*100).toInt()
    return "$loadedBites/$fileBites ($percent%)"
}

suspend fun showLoadingProgress() {
    while (fileIsBeingLoaded()) {
        val progress = getLoadingProgress()
        // use progress
    }
}

Another approach is to use Flow to emit the loading progress. It will look something like the following using flow builder:

fun getLoadingProgress(): Flow<String> = flow {
    while (fileIsBeingLoaded()) {
        delay(4000)
        val percent = ((loadedBites.toDouble() / fileBites.toDouble())*100).toInt()
        emit("$loadedBites/$fileBites ($percent%)")
    }
}

And collect values:

someCoroutineScope.launch {
    getLoadingProgress().collect { progress ->
        // use progress
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM