Skip to content

Async

Regular calls block the handler until the host answers. For long operations (network, file pickers, sending with a wait for the server id) use freshgram.aio: each call goes to the host at once and the answer arrives later.

import freshgram
from freshgram import aio
@freshgram.on_command("weather")
async def weather(event):
response = await aio.net.get("https://example.org/weather")
await aio.ui.toast(response.text()[:100])
  • asyncio does not work here: you can only await awaitables from freshgram.aio.
  • At most 32 calls may wait at the same time.
  • The same permissions and limits as for synchronous calls.
  • aio.files and aio.fs exist only in the async variant.
Function What it does
aio.sleep(seconds) waits without blocking; more than 60 seconds raises Invalid, a negative value waits 0
aio.time() server time
aio.gather(*awaitables) waits for all and returns a list of results in argument order; gather() with no arguments returns []; the first failure is raised, but the other calls keep running
aio.wait_for(awaitable, timeout) waits at most timeout seconds, otherwise TimeoutError. A timeout over 60 is silently cut to 60 (no error); the call itself is not cancelled, its result is dropped
aio.spawn(coroutine) runs a coroutine on the plugin’s loop and returns a Task; an exception in it is written to the plugin log. freshgram.spawn is the same function

gather, wait_for and spawn accept a coroutine (the result of calling an async def function) or a Future returned by freshgram.aio; anything else raises TypeError.

Every freshgram.aio call returns a aio.Future; aio.spawn returns an aio.Task, which is a Future that runs a coroutine. Both can be awaited, and they have these methods:

Method Meaning
done() True once the result or an error is known
result() the result, or raises the stored error; RuntimeError("the result is not ready") if not done()
exception() the error, or None
set_result(value), set_exception(error) finish it by hand (ignored if it is already done)
add_done_callback(function) calls function(future) when done, at once if it is already done

An error from a call (Denied, Invalid, …) is stored in the future and raised by await or result().

task = aio.spawn(refresh()) # background work
first, second = await aio.gather(aio.net.get(a), aio.net.get(b))
done = task.done()

A coroutine also starts if you return it from an event handler: the SDK picks up the CoroutineType.

The low-level mechanism is the "async": true flag in the request. The host answers with a ticket {"ticket": N}, and the result arrives as a call.result event with the same ticket. The Rust SDK offers call_async and sleep_async; the C SDK offers fg_begin_async/fg_send_async/fg_sleep_async.