summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDamien George <damien@micropython.org>2025-09-28 22:28:27 +1000
committerDamien George <damien@micropython.org>2025-09-30 17:39:15 +1000
commita563592b111686dde9b46785bb783d45ec0ba78e (patch)
treeecbe23d63d0647b0eef73b518f3a96e400d14403
parent094000d418e9f7393b3d34814c3e43e6386145a7 (diff)
webassembly/asyncio: Fix ThenableEvent to handle rejected thenables.
Prior to this fix, if a JavaScript thenable/Promise that was part of an asyncio chain was rejected it would be ignored because the Python-side `ThenableEvent` did not register a handler for the rejection. That's fixed by this commit, and a corresponding test added. Signed-off-by: Damien George <damien@micropython.org>
-rw-r--r--ports/webassembly/asyncio/core.py16
-rw-r--r--tests/ports/webassembly/asyncio_top_level_await.mjs4
-rw-r--r--tests/ports/webassembly/asyncio_top_level_await.mjs.exp2
3 files changed, 19 insertions, 3 deletions
diff --git a/ports/webassembly/asyncio/core.py b/ports/webassembly/asyncio/core.py
index 1d0124a6a..9d07bd7b8 100644
--- a/ports/webassembly/asyncio/core.py
+++ b/ports/webassembly/asyncio/core.py
@@ -79,9 +79,8 @@ class TopLevelCoro:
class ThenableEvent:
def __init__(self, thenable):
- self.result = None # Result of the thenable
self.waiting = None # Task waiting on completion of this thenable
- thenable.then(self.set)
+ thenable.then(self.set, self.cancel)
def set(self, value=None):
# Thenable/Promise is fulfilled, set result and schedule any waiting task.
@@ -90,6 +89,15 @@ class ThenableEvent:
_task_queue.push(self.waiting)
self.waiting = None
+ def cancel(self, value=None):
+ # Thenable/Promise is rejected, set error and schedule any waiting task.
+ self.error = jsffi.JsException(
+ value, getattr(value, "name", None), getattr(value, "message", None)
+ )
+ if self.waiting:
+ _task_queue.push(self.waiting)
+ self.waiting = None
+
def remove(self, task):
self.waiting = None
@@ -101,7 +109,9 @@ class ThenableEvent:
cur_task.data = self
# Wait for the thenable to fulfill.
yield
- # Return the result of the thenable.
+ # Raise the error, or return the result, of the thenable.
+ if hasattr(self, "error"):
+ raise self.error
return self.result
diff --git a/tests/ports/webassembly/asyncio_top_level_await.mjs b/tests/ports/webassembly/asyncio_top_level_await.mjs
index 449985b49..fedf10d9c 100644
--- a/tests/ports/webassembly/asyncio_top_level_await.mjs
+++ b/tests/ports/webassembly/asyncio_top_level_await.mjs
@@ -117,6 +117,10 @@ async def main():
# Test top-level waiting on a coro that catches.
await main()
+
+# Test top-level waiting on a task that catches.
+t = asyncio.create_task(main())
+await t
`);
console.log("finished");
diff --git a/tests/ports/webassembly/asyncio_top_level_await.mjs.exp b/tests/ports/webassembly/asyncio_top_level_await.mjs.exp
index 90c817f3d..f6720b5ab 100644
--- a/tests/ports/webassembly/asyncio_top_level_await.mjs.exp
+++ b/tests/ports/webassembly/asyncio_top_level_await.mjs.exp
@@ -22,4 +22,6 @@ jsFail
caught exception: <class 'JsException'> <class 'JsProxy'> ('Error', 'jsFail')
jsFail
caught exception: <class 'JsException'> <class 'JsProxy'> ('Error', 'jsFail')
+jsFail
+caught exception: <class 'JsException'> <class 'JsProxy'> ('Error', 'jsFail')
finished