Skip to content

Commit 5cff2e9

Browse files
Add documentation for run_in_thread timeout parameter
Co-Authored-By: Alek Petuskey <[email protected]>
1 parent f4ad8de commit 5cff2e9

File tree

4 files changed

+171
-1
lines changed

4 files changed

+171
-1
lines changed

docs/api-reference/utils.md

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
```python exec
2+
import reflex as rx
3+
from pcweb import constants, styles
4+
```
5+
6+
# Utility Functions
7+
8+
Reflex provides utility functions to help with common tasks in your applications.
9+
10+
## run_in_thread
11+
12+
The `run_in_thread` function allows you to run a non-async function in a separate thread, which is useful for preventing long-running operations from blocking the UI event queue.
13+
14+
```python
15+
async def run_in_thread(func: Callable, *, timeout: float | None = None) -> Any
16+
```
17+
18+
### Parameters
19+
20+
- `func`: The non-async function to run in a separate thread.
21+
- `timeout`: Maximum number of seconds to wait for the function to complete. If `None` (default), wait indefinitely.
22+
23+
### Returns
24+
25+
- The return value of the function.
26+
27+
### Raises
28+
29+
- `ValueError`: If the function is an async function.
30+
- `asyncio.TimeoutError`: If the function execution exceeds the specified timeout.
31+
32+
### Usage
33+
34+
To use `run_in_thread`, you must call it from within a method decorated with `@rx.event(background=True)`:
35+
36+
```python demo exec id=run_in_thread_demo
37+
import asyncio
38+
import time
39+
import reflex as rx
40+
41+
42+
class RunInThreadState(rx.State):
43+
result: str = "No result yet"
44+
status: str = "Idle"
45+
46+
@rx.event(background=True)
47+
async def run_quick_task(self):
48+
"""Run a quick task that completes within the timeout."""
49+
self.status = "Running quick task..."
50+
51+
def quick_function():
52+
time.sleep(0.5)
53+
return "Quick task completed successfully!"
54+
55+
try:
56+
# Run with a timeout of 2 seconds (more than enough time)
57+
result = await rx.run_in_thread(quick_function, timeout=2.0)
58+
async with self:
59+
self.result = result
60+
self.status = "Idle"
61+
except Exception as e:
62+
async with self:
63+
self.result = f"Error: {str(e)}"
64+
self.status = "Idle"
65+
66+
@rx.event(background=True)
67+
async def run_slow_task(self):
68+
"""Run a slow task that exceeds the timeout."""
69+
self.status = "Running slow task..."
70+
71+
def slow_function():
72+
time.sleep(3.0)
73+
return "This should never be returned due to timeout!"
74+
75+
try:
76+
# Run with a timeout of 1 second (not enough time)
77+
result = await rx.run_in_thread(slow_function, timeout=1.0)
78+
async with self:
79+
self.result = result
80+
self.status = "Idle"
81+
except asyncio.TimeoutError:
82+
async with self:
83+
self.result = "Task timed out after 1 second!"
84+
self.status = "Idle"
85+
except Exception as e:
86+
async with self:
87+
self.result = f"Error: {str(e)}"
88+
self.status = "Idle"
89+
90+
91+
def run_in_thread_example():
92+
return rx.vstack(
93+
rx.heading("run_in_thread Example", size="3"),
94+
rx.text("Status: ", RunInThreadState.status),
95+
rx.text("Result: ", RunInThreadState.result),
96+
rx.hstack(
97+
rx.button(
98+
"Run Quick Task (completes within timeout)",
99+
on_click=RunInThreadState.run_quick_task,
100+
color_scheme="green",
101+
),
102+
rx.button(
103+
"Run Slow Task (exceeds timeout)",
104+
on_click=RunInThreadState.run_slow_task,
105+
color_scheme="red",
106+
),
107+
),
108+
width="100%",
109+
align_items="start",
110+
spacing="4",
111+
)
112+
```
113+
114+
### When to Use run_in_thread
115+
116+
Use `run_in_thread` when you need to:
117+
118+
1. Execute CPU-bound operations that would otherwise block the event loop
119+
2. Call synchronous libraries that don't have async equivalents
120+
3. Prevent long-running operations from blocking UI responsiveness
121+
4. Set a maximum execution time for potentially slow operations
122+
123+
### Example: Processing a Large File
124+
125+
```python
126+
import reflex as rx
127+
import time
128+
129+
class FileProcessingState(rx.State):
130+
progress: str = "Ready"
131+
132+
@rx.event(background=True)
133+
async def process_large_file(self):
134+
self.progress = "Processing file..."
135+
136+
def process_file():
137+
# Simulate processing a large file
138+
time.sleep(5)
139+
return "File processed successfully!"
140+
141+
try:
142+
# Set a timeout of 10 seconds
143+
result = await rx.run_in_thread(process_file, timeout=10.0)
144+
async with self:
145+
self.progress = result
146+
except asyncio.TimeoutError:
147+
async with self:
148+
self.progress = "File processing timed out!"
149+
```
150+
151+
### Notes
152+
153+
- The function passed to `run_in_thread` must be a regular (non-async) function.
154+
- Always use `run_in_thread` within a method decorated with `@rx.event(background=True)`.
155+
- Consider setting appropriate timeouts to prevent indefinite blocking.
156+
- Handle `asyncio.TimeoutError` exceptions to gracefully manage timeouts.

pcweb/components/docpage/sidebar/sidebar_items/reference.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ def get_sidebar_items_api_reference():
1515
api_reference.special_events,
1616
api_reference.browser_storage,
1717
api_reference.browser_javascript,
18+
api_reference.utils,
1819
],
1920
)
2021
]
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
"""Utils API reference page."""
2+
3+
import reflex as rx
4+
5+
from pcweb.templates.docpage import docpage
6+
7+
8+
@docpage("/docs/api-reference/utils/")
9+
def utils():
10+
"""Utils API reference page."""
11+
with open("docs/api-reference/utils.md", encoding="utf-8") as f:
12+
content = f.read()
13+
return rx.markdown(content)

pcweb/whitelist.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
- Incorrect: WHITELISTED_PAGES = ["/docs/getting-started/introduction/"]
1111
"""
1212

13-
WHITELISTED_PAGES = []
13+
WHITELISTED_PAGES = ["/docs/api-reference/utils"]
1414

1515
def _check_whitelisted_path(path: str):
1616
if len(WHITELISTED_PAGES) == 0:

0 commit comments

Comments
 (0)