Skip to content

Commit 016b54d

Browse files
committed
generator style effects
1 parent bb756ad commit 016b54d

File tree

4 files changed

+213
-513
lines changed

4 files changed

+213
-513
lines changed

docs/source/conf.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,7 @@
322322
"sanic": ("https://sanic.readthedocs.io/en/latest/", None),
323323
"tornado": ("https://www.tornadoweb.org/en/stable/", None),
324324
"flask": ("https://flask.palletsprojects.com/en/1.1.x/", None),
325+
"anyio": ("https://anyio.readthedocs.io/en/stable", None),
325326
}
326327

327328
# -- Options for todo extension ----------------------------------------------

docs/source/reference/hooks-api.rst

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -154,27 +154,54 @@ Async Effects
154154
.............
155155

156156
A behavior unique to ReactPy's implementation of ``use_effect`` is that it natively
157-
supports ``async`` functions:
157+
supports ``async`` effects. Async effect functions may either be an async function
158+
or an async generator. If your effect doesn't need to do any cleanup, then you can
159+
simply write an async function.
158160

159161
.. code-block::
160162
161163
async def non_blocking_effect():
162-
resource = await do_something_asynchronously()
163-
return lambda: blocking_close(resource)
164+
await do_something()
164165
165166
use_effect(non_blocking_effect)
166167
168+
However, if you need to do any cleanup, then you must ``yield False`` inside a try-finally
169+
block and place your cleanup logic in the finally block. Yielding ``False`` indicates to
170+
ReactPy that the effect will not yield again before it is cleaned up.
167171

168-
There are **three important subtleties** to note about using asynchronous effects:
172+
.. code-block::
173+
174+
async def blocking_effect():
175+
await do_something()
176+
try:
177+
yield False
178+
finally:
179+
await do_cleanup()
169180
170-
1. The cleanup function must be a normal synchronous function.
181+
use_effect(blocking_effect)
171182
172-
2. Asynchronous effects which do not complete before the next effect is created
173-
following a re-render will be cancelled. This means an
174-
:class:`~asyncio.CancelledError` will be raised somewhere in the body of the effect.
183+
If you have a long-lived effect, you may ``yield True`` multiple times. ``True`` indicates
184+
to ReactPy that the effect will yield again if the effect doesn't need to be cleanup up
185+
yet.
186+
187+
.. code-block::
188+
189+
async def establish_connection():
190+
connection = await open_connection()
191+
try:
192+
while True:
193+
yield False
194+
await connection.send(create_message())
195+
handle_message(await connection.recv())
196+
finally:
197+
await close_connection(connection)
198+
199+
use_effect(non_blocking_effect)
175200
176-
3. An asynchronous effect may occur any time after the update which added this effect
177-
and before the next effect following a subsequent update.
201+
Note that, if an effect needs to be cleaned up, it will only do so when the effect
202+
function yields control back to ReactPy. So you should ensure that either, you can
203+
be sure that the effect will yield in a timely manner, or that you enforce a timeout
204+
on the effect. Otherwise, ReactPy may hang while waiting for the effect to yield.
178205

179206

180207
Manual Effect Conditions

0 commit comments

Comments
 (0)