Skip to content
Version v0.35 ยท supported
[WIP] Pending copyedit and approval.

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.