Tasks¶
A task is a loop that keeps running after the command that started it returns. Most command responses do their work and finish at once. A task is different: it starts a background loop, hands control straight back, and goes on running until something stops it. Use one for anything that has to repeat or wait in the background, such as tapping a key on a timer or holding an input until you say stop.
Starting a task¶
Tasks are started from Lua, under the amri.task namespace. amri.task.spawn(fn,
label) runs a function in the background and gives it a label you can refer to
later. The function should loop, and it should check amri.task.cancelled() so it
can exit cleanly when asked:
[[command]]
name = "auto_run"
description = "Keep tapping forward until told to stop."
lua = "amri.task.spawn(function() while not amri.task.cancelled() do amri.input.tap('w'); sleep_ms(400) end end, 'auto_run')"
[command.trigger]
pattern = "keep running"
Say "keep running" and the loop taps w every 400 milliseconds and keeps doing so.
The command itself returns immediately. The loop stays alive on its own.
There is a related call, amri.task.start(name), which runs another command's
script as a background task by that command's name. spawn is for a loop written
inline or in a library; start is for launching a command you already have.
spawn returns a task handle. Keep it to control that one task directly:
handle:cancel() stops it, handle:wait() waits for it to finish, and
handle:is_running() reports whether it is still active. The full set of task
functions is in the API reference.
Stopping a task¶
A running task keeps going until it is stopped. There are two ways.
The declarative way needs no Lua: give a command a cancels field naming the task
to stop.
[[command]]
name = "stop_running"
description = "Stop the auto-run loop."
cancels = "auto_run"
[command.trigger]
pattern = "stop running"
Say "stop running" and Amri stops the task labelled auto_run. The name in
cancels is the label you gave the task when you started it. A name that matches no
running task does nothing.
The scripted way is the function behind that field: amri.task.stop(name) stops
every task with that label, and amri.task.abort_all() stops all tasks at once.
Cancellation is cooperative
Stopping a task asks it to end at its next sleep_ms or wait, so a loop that
polls amri.task.cancelled() exits on its own terms and can release anything it
holds. A loop that never yields and never checks is still terminated, but it
cannot tidy up first. Give long-running loops a sleep_ms and a
cancelled() check.