kotlinx.coroutines icon indicating copy to clipboard operation
kotlinx.coroutines copied to clipboard

Stabilize Deferred.getCompleted and related

Open cy6erGn0m opened this issue 4 years ago • 3 comments
trafficstars

At the moment, Deferred.getCompleted() and Deferred.getCompletionExceptionOrNull() are experimental and marked with @ExperimentalCoroutinesApi.

The functions above are useful for polling and accessing the deferred result from a non-suspend context where it's impossible to wait for the completion. The related function invokeOnCompletion {} is usually used with these experimental function to access the computation result after its completion. And of course, the callback handler is a regular non-suspend lambda. So without using experimental functions, the only solution is to launch a coroutine in the handler or trigger some existing coroutine (via channel or something). This is too much boilerplate. Fortunately, we have the experimental function providing a way to access the computation result just in place.

The typical usages I've faced are the following.

Generally using the pair looks like:

deferred.invokeOnCompletion { cause ->
    when (cause) {
        null -> deferred.getCompleted()
        else -> ....
    }
}

Handle only if successful:

deferred.invokeOnCompletion { cause ->
    if (cause != null) {
        handle(deferred.getCompleted())
    }
}

getCompletedOrNull:

deferred.invokeOnCompletion { cause ->
    handle(when (cause) {
        null -> deferred.getCompleted()
        else -> null
    })
}

Ensure resource cleanup:

deferred.invokeOnCompletion { cause ->
    if (cause != null) {
        deferred.getCompleted().close()
    }
}

Bridge two deferred:

deferred.invokeOnCompletion { cause ->
    when (cause) {
        null -> publicDeferred.complete(deferred.getCompleted())
        else -> publicDeferred.completeExceptionally(cause)
    }
}

Tracking instant async job status (e.g. in web UI without websockets and instant notification):

onPageLoad {
    when {
        !deferred.isCompleted -> putInProgressLabel()
        deferred.isCancelled -> putCancelledLabel()
        deferred.getCompletionException() != null -> putFailedLabel()
        else -> putSuccessLabel(deferred.getCompled())
}

The alternative API could be

fun Deferred<T>.getCompletionResult(): Result<T>
fun Deferred<T>.invokeOnCompleteionWithResult(handler: (Result<T>) -> Unit)

cy6erGn0m avatar Apr 16 '21 16:04 cy6erGn0m