JS: add suspendCoroutine function
This commit is contained in:
+1
-1
@@ -54,7 +54,7 @@ private var log = ""
|
|||||||
|
|
||||||
private var inAwait = false
|
private var inAwait = false
|
||||||
|
|
||||||
suspend fun <S> await(value: Promise<S>): S = CoroutineIntrinsics.suspendCoroutineOrReturn { continuation: Continuation<S> ->
|
suspend fun <S> await(value: Promise<S>): S = suspendCoroutine { continuation ->
|
||||||
if (inAwait) {
|
if (inAwait) {
|
||||||
throw IllegalStateException("Can't call await recursively")
|
throw IllegalStateException("Can't call await recursively")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,6 +73,21 @@ public fun <T> (suspend () -> T).startCoroutine(
|
|||||||
createCoroutine(completion, dispatcher).resume(Unit)
|
createCoroutine(completion, dispatcher).resume(Unit)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains the current continuation instance inside suspend functions and suspends
|
||||||
|
* currently running coroutine.
|
||||||
|
*
|
||||||
|
* In this function both [Continuation.resume] and [Continuation.resumeWithException] can be used either synchronously in
|
||||||
|
* the same stack-frame where suspension function is run or asynchronously later in the same thread or
|
||||||
|
* from a different thread of execution. Repeated invocation of any resume function produces [IllegalStateException].
|
||||||
|
*/
|
||||||
|
@SinceKotlin("1.1")
|
||||||
|
public suspend fun <T> suspendCoroutine(block: (Continuation<T>) -> Unit): T = CoroutineIntrinsics.suspendCoroutineOrReturn { c ->
|
||||||
|
val safe = SafeContinuation(c)
|
||||||
|
block(safe)
|
||||||
|
safe.getResult()
|
||||||
|
}
|
||||||
|
|
||||||
// ------- internal stuff -------
|
// ------- internal stuff -------
|
||||||
|
|
||||||
internal interface DispatchedContinuation {
|
internal interface DispatchedContinuation {
|
||||||
@@ -151,4 +166,60 @@ private class ContinuationFacade(val innerContinuation: Continuation<Any?>, val
|
|||||||
innerContinuation.resumeWithException(exception)
|
innerContinuation.resumeWithException(exception)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val UNDECIDED: Any? = Any()
|
||||||
|
private val RESUMED: Any? = Any()
|
||||||
|
private class Fail(val exception: Throwable)
|
||||||
|
|
||||||
|
internal class SafeContinuation<in T> internal constructor(private val delegate: Continuation<T>) : Continuation<T> {
|
||||||
|
private var result: Any? = UNDECIDED
|
||||||
|
|
||||||
|
override fun resume(value: T) {
|
||||||
|
when (result) {
|
||||||
|
UNDECIDED -> {
|
||||||
|
result = value
|
||||||
|
}
|
||||||
|
CoroutineIntrinsics.SUSPENDED -> {
|
||||||
|
result = RESUMED
|
||||||
|
delegate.resume(value)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
throw IllegalStateException("Already resumed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun resumeWithException(exception: Throwable) {
|
||||||
|
when (result) {
|
||||||
|
UNDECIDED -> {
|
||||||
|
result = Fail(exception)
|
||||||
|
}
|
||||||
|
CoroutineIntrinsics.SUSPENDED -> {
|
||||||
|
result = RESUMED
|
||||||
|
delegate.resumeWithException(exception)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
throw IllegalStateException("Already resumed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun getResult(): Any? {
|
||||||
|
if (result == UNDECIDED) {
|
||||||
|
result = CoroutineIntrinsics.SUSPENDED
|
||||||
|
}
|
||||||
|
val result = this.result
|
||||||
|
return when (result) {
|
||||||
|
RESUMED -> {
|
||||||
|
CoroutineIntrinsics.SUSPENDED // already called continuation, indicate SUSPENDED upstream
|
||||||
|
}
|
||||||
|
is Fail -> {
|
||||||
|
throw result.exception
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
result // either SUSPENDED or data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user