Owned asynchronous tasks
Enable the tasks feature on argui-runtime. Tokio is the native backend; Web uses
browser-local futures and does not bundle Tokio. One lazy native executor is
shared by the application's windows, with at most four worker threads.
ModelContext::spawn(future, callback) returns Result<TaskHandle, TaskError>.
Keep the handle while the operation is wanted. Dropping it, cancelling it,
destroying its shared model, or closing its entity resource scope cancels delivery.
Hiding a retained component is not destruction. Closing one presentation does not
cancel shared-model work retained by another presentation.
Context::spawn additionally binds work to the calling presentation. Its callback
can use that presentation's services; unmounting cancels even queued delivery.
Use spawn_in(scope, future, callback) on either context for an additional lifetime
constraint, not a transfer of ownership. Mount destruction closes its scope; model
destruction cancels all of its remaining tasks. Runtime shutdown cancels delivery
regardless of surviving model or mount handles. There is no subtree-wide task
cancellation API: ownership follows explicit resource scopes.
For presentation-owned operations, Mount::spawn(future, callback) applies both
model and mount lifetimes automatically. Mount::spawn_latest replaces a
TaskSlot with the same cancellation rules. Completion uses the exact mount's
context and environment; a dead mount is never recreated through its model.
Both APIs reject a closed scope with TaskError::ScopeClosed, and require an
attached executor just like model tasks. Retain their handle or slot while the
operation is wanted.
spawn_latest(&mut slot, future, callback) on either context replaces a retained
TaskSlot. Even an older result already queued for delivery is discarded.
The callback receives the component, the typed result and the same kind of
context that launched the task (ModelContext or presentation Context). Only
the callback runs on the UI thread; native futures/results must be Send.
Call cx.notify() when a result changes visible state.
Start tasks from handlers or updates after the Entity is attached to its host,
not on every render. A detached Entity or plain default Context returns
TaskError::Unavailable. Custom hosts attach a TaskRuntime using
Entity::set_task_runtime, call drain() when its wake callback fires, and
use AppModel::tasks_ready to collect application invalidations and commands.
Call shutdown() when the host ends.
Errors returned by the future remain ordinary typed results inside the task
result. Native unwinding panics become TaskError::Panicked; aborting panics
(including browser traps) cannot be recovered.
The dispatcher accepts at most 256 outstanding tasks and delivers at most 64
completions per wake. It uses a bounded completion channel and coalesced wakes.
Spawning while full returns CapacityExceeded. No polling or permanent
animation timer runs. tasks::sleep and tasks::yield_now wake active work.
Native spawn_blocking passes a cooperative CancellationToken. A started
blocking function cannot be forcibly interrupted: check the token and ensure
the operation terminates. Cancellation suppresses its callback even if the
function finishes later. Browser code uses async operations or cooperative
chunks; this API does not create Web Workers.
The current Solid and React gallery has no async-tasks example. Exercise task
ownership and cancellation through the argui-runtime model tests, then test
the application-specific async result and error states in the consuming app.