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>
This commit is contained in:
Damien George
2025-09-28 22:28:27 +10:00
parent 094000d418
commit a563592b11
3 changed files with 19 additions and 3 deletions

View File

@@ -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

View File

@@ -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");

View File

@@ -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