From 6b735461255ce8446cc926d6b2008f36bd6188f3 Mon Sep 17 00:00:00 2001 From: Alex Shepeliev Date: Mon, 9 Dec 2024 14:40:02 +0200 Subject: [PATCH] Fix Promise.await() never resuming if the promise rejected with a non-Throwable (#4120) --- kotlinx-coroutines-core/js/src/Promise.kt | 2 +- .../js/test/PromiseTest.kt | 23 +++++++++++++++++++ .../wasmJs/test/PromiseTest.kt | 23 +++++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/kotlinx-coroutines-core/js/src/Promise.kt b/kotlinx-coroutines-core/js/src/Promise.kt index f84338a888..5eb93d348e 100644 --- a/kotlinx-coroutines-core/js/src/Promise.kt +++ b/kotlinx-coroutines-core/js/src/Promise.kt @@ -63,5 +63,5 @@ public fun Promise.asDeferred(): Deferred { public suspend fun Promise.await(): T = suspendCancellableCoroutine { cont: CancellableContinuation -> this@await.then( onFulfilled = { cont.resume(it) }, - onRejected = { cont.resumeWithException(it) }) + onRejected = { cont.resumeWithException(it as? Throwable ?: Exception("Non-Kotlin exception $it")) }) } diff --git a/kotlinx-coroutines-core/js/test/PromiseTest.kt b/kotlinx-coroutines-core/js/test/PromiseTest.kt index 319778dddb..f9cb0ed56d 100644 --- a/kotlinx-coroutines-core/js/test/PromiseTest.kt +++ b/kotlinx-coroutines-core/js/test/PromiseTest.kt @@ -83,4 +83,27 @@ class PromiseTest : TestBase() { if (seq != 1) error("Unexpected result: $seq") } } + + @Test + fun testAwaitPromiseRejectedWithNonKotlinException() = GlobalScope.promise { + lateinit var r: (dynamic) -> Unit + val toAwait = Promise { _, reject -> r = reject } + val throwable = async(start = CoroutineStart.UNDISPATCHED) { + assertFails { toAwait.await() } + } + r("Rejected") + assertContains(throwable.await().message ?: "", "Rejected") + } + + @Test + fun testAwaitPromiseRejectedWithKotlinException() = GlobalScope.promise { + lateinit var r: (dynamic) -> Unit + val toAwait = Promise { _, reject -> r = reject } + val throwable = async(start = CoroutineStart.UNDISPATCHED) { + assertFails { toAwait.await() } + } + r(RuntimeException("Rejected")) + assertIs(throwable.await()) + assertEquals("Rejected", throwable.await().message) + } } diff --git a/kotlinx-coroutines-core/wasmJs/test/PromiseTest.kt b/kotlinx-coroutines-core/wasmJs/test/PromiseTest.kt index 63550439eb..e72e661517 100644 --- a/kotlinx-coroutines-core/wasmJs/test/PromiseTest.kt +++ b/kotlinx-coroutines-core/wasmJs/test/PromiseTest.kt @@ -84,4 +84,27 @@ class PromiseTest : TestBase() { null } } + + @Test + fun testAwaitPromiseRejectedWithNonKotlinException() = GlobalScope.promise { + lateinit var r: (JsAny) -> Unit + val toAwait = Promise { _, reject -> r = reject } + val throwable = async(start = CoroutineStart.UNDISPATCHED) { + assertFails { toAwait.await() } + } + r("Rejected".toJsString()) + assertIs(throwable.await()) + } + + @Test + fun testAwaitPromiseRejectedWithKotlinException() = GlobalScope.promise { + lateinit var r: (JsAny) -> Unit + val toAwait = Promise { _, reject -> r = reject } + val throwable = async(start = CoroutineStart.UNDISPATCHED) { + assertFails { toAwait.await() } + } + r(RuntimeException("Rejected").toJsReference()) + assertIs(throwable.await()) + assertEquals("Rejected", throwable.await().message) + } }