diff options
| author | Damien George <damien@micropython.org> | 2025-09-28 22:28:27 +1000 |
|---|---|---|
| committer | Damien George <damien@micropython.org> | 2025-09-30 17:39:15 +1000 |
| commit | a563592b111686dde9b46785bb783d45ec0ba78e (patch) | |
| tree | ecbe23d63d0647b0eef73b518f3a96e400d14403 | |
| parent | 094000d418e9f7393b3d34814c3e43e6386145a7 (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.py | 16 | ||||
| -rw-r--r-- | tests/ports/webassembly/asyncio_top_level_await.mjs | 4 | ||||
| -rw-r--r-- | tests/ports/webassembly/asyncio_top_level_await.mjs.exp | 2 |
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 |
