-
Notifications
You must be signed in to change notification settings - Fork 1
Async Await
astal-lua provides the async and await decorators to simplify asynchronous I/O operations, allowing functions to run in async-enabled contexts. Internally, they rely on Gio.Async, which transforms Gio-style asynchronous functions into synchronous-like code.
In lgi, many async operations follow a callback-based pattern:
-
name_asyncstarts the operation and registers a callback. -
name_finishis called inside the callback to retrieve the result.
While common in Gio, this can lead to deeply nested callback chains (callback hell). Gio.Async allows you to wrap these functions using Lua coroutines, so you can write linear, non-blocking code.
The async decorator wraps a function to accept a callback. The function inside does not return a value, so the result must be handled via the callback, similar to Gio’s _async functions.
---@generic F: function
---@param fn F
---@return F
local async = function(fn)
return function(...) return Gio.Async.start(fn)(...) end
end---@async
local async_read_file = function(filename)
local file = Gio.File.new_for_path(filename)
local info = file:async_query_info('standard::size', 'NONE')
local stream = file:async_read()
local bytes = stream:async_read_bytes(info:get_size())
assert(stream:async_close())
return bytes.data
end
local read_file_async = async(function(filename, callback)
callback(async_read_file(filename))
end)
read_file_async('/etc/passwd', print) -- workslocal function ExampleButton()
return Widget.Button {
label = 'Dump!',
on_clicked = async(function()
local contents = async_read_file('/etc/passwd')
print(contents)
end),
}
endWarning
Calling an async_ function outside an async-enabled context will not work.
The await decorator allows calling async functions and returning their result directly, without using callbacks. The parent function must be executed in an async-enabled context (---@async).
---@generic F: function
---@param fn F
---@return F
local await = function(fn)
return function(...) return Gio.Async.call(fn)(...) end
endUnlike async, which schedules a function in the background, await blocks only the coroutine, not the main thread, keeping the UI responsive.
local read_file = await(function(path)
return async_read_file(path)
end)
local function OtherExampleButton()
return Widget.Button {
label = 'DumpAgain!',
on_clicked = function()
local contents = read_file('/etc/passwd') -- This wont block the UI
print(contents)
end,
}
endWarning
Do not call an await function inside another async function via a wrapper. For example:
local async_fn = function() end
local fn = await(function() return async_fn() end)
local example = async(function()
local result = fn() -- ❌ Don't do this
end)Instead, call the original async function directly:
local example = async(function()
local result = async_fn() -- ✅ Correct
end)