Skip to content

Commit 362e80d

Browse files
committed
functional effect timeout parameter
1 parent a6e371a commit 362e80d

File tree

1 file changed

+25
-45
lines changed

1 file changed

+25
-45
lines changed

src/reactpy/core/hooks.py

Lines changed: 25 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -137,21 +137,21 @@ def use_effect(
137137
hook = current_hook()
138138
dependencies = _try_to_infer_closure_values(function, dependencies)
139139
memoize = use_memo(dependencies=dependencies)
140-
unmount_func: Ref[_EffectCleanFunc | None] = use_ref(None)
140+
cleanup_func: Ref[_EffectCleanFunc | None] = use_ref(None)
141141

142142
def decorator(func: _SyncEffectFunc) -> None:
143143
async def effect(stop: asyncio.Event) -> None:
144-
if unmount_func.current:
145-
unmount_func.current()
146-
unmount_func.current = None
144+
if cleanup_func.current:
145+
cleanup_func.current()
146+
cleanup_func.current = None
147147

148148
# Execute the effect and store the clean-up function
149-
unmount = unmount_func.current = func()
149+
cleanup_func.current = func()
150150

151151
# Run the clean-up function when the effect is stopped
152152
await stop.wait()
153-
if unmount:
154-
unmount()
153+
if cleanup_func.current:
154+
cleanup_func.current()
155155

156156
return memoize(lambda: hook.add_effect(effect))
157157

@@ -179,54 +179,34 @@ def use_async_effect(
179179
def use_async_effect(
180180
function: _AsyncEffectFunc | None = None,
181181
dependencies: Sequence[Any] | ellipsis | None = ...,
182+
shutdown_timeout: float = 0.1,
182183
) -> Callable[[_AsyncEffectFunc], None] | None:
183-
"""See the full :ref:`Use Effect` docs for details
184-
185-
Parameters:
186-
function:
187-
Applies the effect and can return a clean-up function
188-
dependencies:
189-
Dependencies for the effect. The effect will only trigger if the identity
190-
of any value in the given sequence changes (i.e. their :func:`id` is
191-
different). By default these are inferred based on local variables that are
192-
referenced by the given function.
193-
194-
Returns:
195-
If not function is provided, a decorator. Otherwise ``None``.
196-
"""
197184
hook = current_hook()
198185
dependencies = _try_to_infer_closure_values(function, dependencies)
199186
memoize = use_memo(dependencies=dependencies)
200-
unmount_func: Ref[_EffectCleanFunc | None] = use_ref(None)
187+
cleanup_func: Ref[_EffectCleanFunc | None] = use_ref(None)
201188

202189
def decorator(func: _AsyncEffectFunc) -> None:
203-
def sync_executor() -> _EffectCleanFunc | None:
204-
task = asyncio.create_task(func())
205-
206-
def unmount_executor() -> None:
207-
if not task.cancel():
208-
try:
209-
unmount = task.result()
210-
except asyncio.CancelledError:
211-
pass
212-
else:
213-
if unmount:
214-
unmount()
215-
216-
return unmount_executor
217-
218190
async def effect(stop: asyncio.Event) -> None:
219-
if unmount_func.current:
220-
unmount_func.current()
221-
unmount_func.current = None
191+
if cleanup_func.current:
192+
cleanup_func.current()
193+
cleanup_func.current = None
222194

223-
# Execute the effect and store the clean-up function
224-
unmount = unmount_func.current = sync_executor()
195+
# Execute the effect in a background task
196+
task = asyncio.create_task(func())
225197

226-
# Run the clean-up function when the effect is stopped
198+
# Wait until the effect is stopped
227199
await stop.wait()
228-
if unmount:
229-
unmount()
200+
201+
# Try to fetch the results of the task
202+
results, _ = await asyncio.wait([task], timeout=shutdown_timeout)
203+
if results:
204+
cleanup_func.current = results.pop().result()
205+
if cleanup_func.current:
206+
cleanup_func.current()
207+
208+
# Cancel the task if it's still running
209+
task.cancel()
230210

231211
return memoize(lambda: hook.add_effect(effect))
232212

0 commit comments

Comments
 (0)