Async runtime¶
PythonNative runs a single framework-wide asyncio event loop on a
dedicated daemon thread. Every awaitable surface in the framework —
use_async_effect,
use_query,
use_mutation,
fetch,
AsyncStorage, the awaitable native
modules (Camera /
Location /
Notifications),
and Animated composites — schedules
its work on this loop.
Asyncio runtime for PythonNative.
PythonNative runs a single, framework-wide asyncio event loop on
a dedicated daemon thread. Every awaitable surface in the framework
— the async hooks
(use_async_effect,
use_query,
use_mutation), the
fetch HTTP client,
AsyncStorage, the awaitable
native modules
(Camera /
Location /
Notifications),
and awaited animations — schedules its work on this loop via
run_async.
The reconciler is not asyncio-aware; it still runs synchronously on
the platform main thread. Coroutines that want to mutate component
state simply call the regular use_state setter, and the existing
deferred-render path inside the screen host marshals the re-render
onto the main thread. The runtime is therefore additive: it gives
coroutines somewhere to live without changing the rendering contract.
Example
Functions:
| Name | Description |
|---|---|
get_loop |
Return the framework-wide event loop, starting it on first use. |
run_async |
Schedule |
call_threadsafe |
Schedule |
resolve_future |
Set |
reject_future |
Set |
create_future |
Create a future bound to the framework runtime loop. |
call_on_main_thread |
Run |
get_loop
¶
get_loop() -> AbstractEventLoop
Return the framework-wide event loop, starting it on first use.
The loop runs on a daemon thread ("pn-asyncio") and lives for
the duration of the process. It is safe to call this from any
thread.
Returns:
| Type | Description |
|---|---|
AbstractEventLoop
|
The shared :class: |
run_async
¶
Schedule awaitable on the framework loop and return a thread future.
Use this when calling async code from synchronous code (e.g. an
event handler, a hook setup function, or a test). The returned
:class:concurrent.futures.Future is created by
:func:asyncio.run_coroutine_threadsafe so it can be result()-ed
from the calling thread and cancel()-ed from anywhere.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
awaitable
|
Awaitlike[T]
|
Either a coroutine object (the typical case) or any
awaitable. Awaitables that are not coroutines are wrapped
with :func: |
required |
Returns:
| Type | Description |
|---|---|
'_ThreadFuture[T]'
|
A thread-safe future that resolves with the coroutine's return |
'_ThreadFuture[T]'
|
value, or raises its exception. |
call_threadsafe
¶
Schedule callback(*args) on the loop thread.
Thin wrapper around
:meth:asyncio.AbstractEventLoop.call_soon_threadsafe. Useful from
native delegates (which may fire on arbitrary threads) when you
need to hop onto the runtime thread before touching asyncio
primitives.
resolve_future
¶
Set future's result from any thread (no-op if already done).
Convenience used by every native delegate that wraps a callback into an awaitable: the delegate doesn't have to know which thread it's on, only that it must not race with cancellation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
future
|
'asyncio.Future[T]'
|
An :class: |
required |
value
|
T
|
The value to deliver as the future's result. |
required |
reject_future
¶
reject_future(future: 'asyncio.Future[Any]', error: BaseException) -> None
Set future's exception from any thread (no-op if already done).
create_future
¶
Create a future bound to the framework runtime loop.
Safe to call from any thread. The returned future is not
attached to whatever loop is current on the caller; instead it
lives on the framework's shared loop so any thread can call
resolve_future /
reject_future on it.
call_on_main_thread
¶
call_on_main_thread(fn: Callable[[], None]) -> None
Run fn() on the platform UI thread.
- iOS: dispatches
fnonto the main dispatch queue vialibdispatch.dispatch_async_f(called through :class:ctypes.PyDLLto keep the GIL held — see the_ios_call_on_maincomment block for why this matters). - Android: posts a
RunnabletoHandler(Looper.getMainLooper()). - Desktop / tests: runs
fn()inline.
Exceptions raised by fn are caught and printed; they must not
propagate into UIKit / the Android Looper. If you need to surface
a result back to the asyncio loop, do it via
resolve_future from
inside fn.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fn
|
Callable[[], None]
|
A zero-arg callable. Runs on the main thread when the platform's UI runtime is available, otherwise inline. |
required |
Pattern: bridge a sync handler into async code¶
import pythonnative as pn
@pn.component
def Toolbar():
async def export():
report = await build_report()
await save_to_disk(report)
return pn.Button("Export", on_click=lambda: pn.run_async(export()))
Next steps¶
- Walk through the async surface end-to-end: Async + data guide.