edifice.use_async#

edifice.use_async(fn_coroutine, dependencies)[source]#

Asynchronous side-effect Hook inside a @component function.

Will create a new Task with the fn_coroutine coroutine.

The fn_coroutine will be called every time the dependencies change. Only one fn_coroutine will be allowed to run at a time.

use_async to fetch from the network#
@component
def WordDefinition(self, word:str):
    definition, definition_set = use_state("")

    async def fetcher():
        try:
            definition_set("Fetch definition pending")
            x = await fetch_definition_from_the_internet(word)
            definition_set(x)
        except asyncio.CancelledError:
            definition_set("Fetch definition cancelled")
            raise
        except BaseException:
            defintion_set("Fetch definition failed")

    cancel_fetcher = use_async(fetcher, word)

    with VBoxView():
        Label(text=word)
        Label(text=definition)
        Button(text="Cancel fetch", on_click=lambda _:cancel_fetcher())

Cancellation#

The async fn_coroutine Task can be cancelled by Edifice. Edifice will call cancel() on the Task. See also Task Cancellation.

  1. If the dependencies change before the fn_coroutine Task completes, then the fn_coroutine Task will be cancelled and then the new fn_coroutine Task will be started after the old fn_coroutine Task completes.

  2. The use_async Hook returns a function which can be called to cancel the fn_coroutine Task manually. In the example above, the _cancel_fetcher() function can be called to cancel the fetcher.

  3. If the component is unmounted before the fn_coroutine Task completes, then the fn_coroutine Task will be cancelled.

Write your async fn_coroutine function in such a way that it cleans itself up after exceptions. If you catch a CancelledError then always re-raise it.

You may call a use_state() setter during a CancelledError exception. If the fn_coroutine Task was cancelled because the component is being unmounted, then the use_state() setter will have no effect.

Timers#

The use_async Hook is also useful for timers and animation.

Here is an example which shows how to run a timer in a component. The Harmonic Oscillator in Examples uses this technique:

is_playing, is_playing_set = use_state(False)
play_trigger, play_trigger_set = use_state(False)

async def play_tick():
    if is_playing:
        # Do the timer effect here
        # (timer effect code)

        # Then wait for 0.05 seconds and trigger another play_tick.
        await asyncio.sleep(0.05)
        play_trigger_set(lambda p: not p)

use_async(play_tick, (is_playing, play_trigger))

Button(
    text="pause" if is_playing else "play",
    on_click=lambda e: is_playing_set(lambda p: not p),
)
type fn_coroutine:

Callable[[], Coroutine[None, None, None]]

param fn_coroutine:

Async Coroutine function to be run as a Task.

type dependencies:

Any

param dependencies:

The fn_coroutine Task will be started when the dependencies are not __eq__ to the old dependencies.

rtype:

Callable[[], None]

returns:

A function which can be called to cancel the fn_coroutine Task manually.