AJ Alt
10/30/2019, 6:24 PMCoroutineScope.future
or rxSingle
functions from the integrations libraries. Those functions are all implemented with AbstractCoroutine
, which is marked as internal. Is there a recommended way to implement that type of functionality without internal classes, or is there no way around internal APIs a the moment?Ilmir Usmanov [JB]
10/30/2019, 6:36 PMimport kotlin.coroutines.*
class AsyncContinuation<T>: Continuation<T> {
var result: T? = null
override val context: CoroutineContext
get() = EmptyCoroutineContext
override fun resumeWith(result: Result<T>) {
this.result = result.getOrThrow()
}
}
class Async<T>(val ac: AsyncContinuation<T>) {
suspend fun await(): T =
suspendCoroutine<T> {
it.resume(ac.result!!)
}
}
fun <T> async(c: suspend () -> T): Async<T> {
val ac = AsyncContinuation<T>()
c.startCoroutine(ac)
return Async(ac)
}
suspend fun main() {
val async = async {
"OK"
}
println(async.await())
}
Of course, this is just an example and it has some drawbacks: 1. It does not support structured concurrency; 2. It is single threaded; 3. await
is still suspend
.octylFractal
10/30/2019, 6:40 PMrunBlocking
AJ Alt
10/30/2019, 6:43 PMAJ Alt
10/30/2019, 6:49 PMfun <T> future(coro: suspend () -> T): MyCompletableFuture<T>
which is a bit different from that example.octylFractal
10/30/2019, 6:56 PMCoroutineScope
, calling launch
and completing it inside the coroutine?Dico
10/30/2019, 7:30 PMDico
10/30/2019, 7:33 PMawait
gets called.Dico
10/30/2019, 7:34 PMDeferred
value.Dico
10/30/2019, 7:36 PMsuspend fun await() = suspendCoroutine { it.resume(result!!) }
suspend fun await() = result!!
The continuation it
needs to be registered somewhere and resumed when the result is known.Ilmir Usmanov [JB]
10/30/2019, 8:02 PMimport kotlin.coroutines.*
class AsyncContinuation<T>: Continuation<T> {
var result: T? = null
var completion: Continuation<T>? = null
override val context: CoroutineContext
get() = EmptyCoroutineContext
override fun resumeWith(result: Result<T>) {
this.result = result.getOrThrow()
completion?.resumeWith(result)
}
}
class Async<T>(val ac: AsyncContinuation<T>) {
suspend fun await(): T =
suspendCoroutine<T> {
val res = ac.result
if (res != null)
it.resume(res)
else
ac.completion = it
}
}
fun <T> async(c: suspend () -> T): Async<T> {
val ac = AsyncContinuation<T>()
c.startCoroutine(ac)
return Async(ac)
}
fun builder(c: suspend() -> Unit) {
c.startCoroutine(object : Continuation<Unit>{
override val context: CoroutineContext
get() = EmptyCoroutineContext
override fun resumeWith(result: Result<Unit>) {
result.getOrThrow()
}
})
}
fun main() {
var c: Continuation<String>? = null
builder {
val async = async {
suspendCoroutine<String> { c = it }
}
println(async.await())
}
c?.resume("OK")
}
AJ Alt
10/30/2019, 8:13 PMIlmir Usmanov [JB]
10/30/2019, 8:14 PMoverride fun resumeWith(result: Result<T>) {
this.result = result.getOrThrow()
completion?.resumeWith(result)
}
It is better to throw exception only if the lambda did not suspend. I.e. rewrite it to something like
override fun resumeWith(result: Result<T>) {
if (completion != null) completion!!.resumeWith(result)
else this.result = result.getOrThrow()
}
Dico
10/31/2019, 9:01 AMawait
is called so it can be passed down no?Ilmir Usmanov [JB]
10/31/2019, 9:53 AMasync
in the example is greedy, there is no harm to throw the exception right away (especially, taking into account, that calling await
is not necessary). If the lambda did not suspend, there is no reason to store the exception, AsyncContinuation
is root continuation anyway. If the lambda (and, consequently, await
) did suspend, we need to resume await
, in order not to leak its continuation.Dico
11/01/2019, 2:02 PMgetOrThrow
usage.
In the event that this throws (when there isn't a continuation yet), await
will never resume. Surely you want this to be thrown in the stack frame of await
caller. This is problematic when await
is not called immediately.Ilmir Usmanov [JB]
11/01/2019, 2:04 PMDico
11/01/2019, 2:07 PMDico
11/01/2019, 2:09 PM