TaskManager
The TaskManager module allows for scheduling code.
Many functions in this module take as argument a function to call and a list of arguments to call it with. Remember that if you are using classes, self should be passed as the first argument.
Require: require("Starlit/TaskManager")
Function
-
Starlit.TaskManager.addTaskChain(name:
string): (chain:starlit.taskmanager.TaskChain) Added in version v1.5.0.
Creates a task chain.
It is recommended to create a task chain for each file/module that needs one rather than sharing one across your mod. Unrelated modules rarely have a good reason to interact with each other’s tasks directly, and chains are very lightweight.
Task chains should not be disposed of: the task manager holds a reference to all created chains that will not be removed until Lua reloads, therefore doing so results in a memory leak.
- Parameters:
name (
string) – Name of the task chain. The format ‘modname.modulename’ is recommended to avoid overlap.
-
Starlit.TaskManager.delayTicks<T>(func: fun(...:
T...), ticks:integer, ...:T...):nil Creates a task to call a function after a delay of N ticks.
- Parameters:
func (fun(...:
T...)) – The function to call.ticks (
integer) – The amount of ticks to delay the calling by.... (
T...) – Any arguments to the function.
-
Starlit.TaskManager.repeatEveryTicks<T>(func: fun(...:
T...), ticks:integer, ...:T...):nil Creates a task to repeat a function every N ticks. Note that it is not guaranteed that every invocation is exactly the given number of ticks apart.
- Parameters:
func (fun(...:
T...)) – The function to call.ticks (
integer) – How often, in ticks, to call the function.... (
T...) – Any arguments to the function.
Enum
-
enum starlit.taskmanager.TaskResult
Added in version v1.5.0.
Result values for a task. Determines what the task manager will do with the task after it completes. If a task doesn’t return one of these values an error will be raised!
-
DONE:
string Remove the task from the task manager.
-
CONTINUE:
string Perform the task again next tick.
-
DONE:
Class
-
class starlit.taskmanager.TaskChain
-
addTask<T>(
self,
func: fun(...:T...):starlit.taskmanager.TaskResult,
...:T...
): (task:starlit.taskmanager.Task) Adds a task to the chain. The task’s function will be called every tick until it returns
starlit.taskmanager.TaskResult.DONE.- Parameters:
func (fun(...:
T...):starlit.taskmanager.TaskResult) – Function to call when running the task.... (
T...) – Arguments to call func with.
- Returns:
task (
starlit.taskmanager.Task) – Handle to the task. This can be used to query the task later.
-
removeTask(self, task:
starlit.taskmanager.Task):nil Removes a task.
A task from another chain has an undefined result: only pass tasks created by this chain.
- Parameters:
task (
starlit.taskmanager.Task) – Task to remove.
-
hasTask(self, task?:
starlit.taskmanager.Task): (hasTask:boolean) Returns whether this chain has a task.
A task from another chain has an undefined result: only pass tasks created by this chain.
- Parameters:
task? (
starlit.taskmanager.Task) – Task to check. Nil will always result infalse.- Returns:
hasTask (
boolean) – Whether the task is still running. If not, then the task has ended or been removed.
-
addTask<T>(
Examples
Tasks are functions that, once added, will be called every tick until finished. An example of a simple task that counts to 5, incrementing by one each tick, printing each number to the console:
local TaskManager = require("Starlit/Taskmanager")
-- cache this so we don't have to type out the whole thing every time
local TaskResult = TaskManager.TaskResult
local taskChain = TaskManager.addTaskChain("mymod.mymodule")
local count = 0
local function task()
count = count + 1
print(count)
if count == 5 then
-- reset the count for next time
count = 0
-- return DONE to tell the task manager to stop running this function every tick
return TaskResult.DONE
end
-- continue running the task on the next tick
return TaskResult.CONTINUE
end
-- start running the task next tick
taskChain:addTask(task)
An issue with this basic example is that count escapes the function, so we can’t run more than one of the same task at the same time,
they would share the same counter.
To avoid this, we could create an object to store the state of the function, however this adds a lot of complexity to a very simple program.
An alternative solution would be to make our function async, keeping all of the state within the function. An async function is essentially a function that can be paused, refered to as ‘yielding’. To call an async function, we have to create a coroutine with that function. Async functions are very suitable for tasks.
local TaskManager = require("Starlit/Taskmanager")
local TaskResult = TaskManager.TaskResult
local taskChain = TaskManager.addTaskChain("mymod.mymodule")
-- When using EmmyLua, the @async annotation tells the language server that this is an async function:
-- an async function is a function that is meant to be used in a coroutine.
-- This will generate a warning when you try to call it outside of another async function:
-- This is important because coroutine.yield will throw an error if you aren't in a coroutine!
---@async
local function task()
for i = 1, 5 do
print(i)
-- coroutine.yield pauses the function
-- CONTINUE tells the task manager we want to resume from here next tick
coroutine.yield(TaskResult.CONTINUE)
end
return TaskResult.DONE
end
-- we can now add as many of the task as we want without issue:
taskChain:addTask(
-- we cannot pass an async function directly, we must create a new coroutine for that function with coroutine.wrap
coroutine.wrap(task)
)
taskChain:addTask(
coroutine.wrap(task)
)
While the concepts of async functions and coroutines may be more difficult to grasp at first, you can clearly see how they allow us to write tasks with much more natural and simple code.
For a real world example of a task, see WaterGoesBad, which uses a task to stagger object updates over several ticks (particularly the update and startUpdate functions).