JS: add suspendCoroutine function

This commit is contained in:
Alexey Andreev
2016-12-22 19:23:54 +03:00
parent 2edb1ff081
commit 11ce184d8e
2 changed files with 73 additions and 2 deletions
+1 -1
View File
@@ -54,7 +54,7 @@ private var log = ""
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) {
throw IllegalStateException("Can't call await recursively")
}
+72 -1
View File
@@ -73,6 +73,21 @@ public fun <T> (suspend () -> T).startCoroutine(
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 interface DispatchedContinuation {
@@ -151,4 +166,60 @@ private class ContinuationFacade(val innerContinuation: Continuation<Any?>, val
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
}
}
}
}