app.thread - Concurrency/Coroutine API
Background tasks and concurrency control.
Note: The thread API is now under the
app.threadnamespace.
Methods
app.thread.create(func)
Creates and starts a background coroutine.
Parameters:
func(function) - Function to execute in the background
Returns: number - Coroutine ID
local tid = app.thread.create(function()
-- Time-consuming operations (e.g., shell commands, HTTP requests)
local result = app.shell.execute("find ~/Downloads -name '*.zip'")
return result
end)
app.thread.wait(tid, timeout?)
Waits for a coroutine to complete and gets its result.
Parameters:
tid(number) - Coroutine IDtimeout(number, optional) - Timeout in milliseconds
Returns: any - Coroutine return value
local tid = app.thread.create(function()
app.thread.sleep(2000) -- 2 seconds
return "Done"
end)
local result = app.thread.wait(tid) -- Blocking wait
app.log.info(result) -- "Done"
-- With timeout
local result = app.thread.wait(tid, 5000) -- Wait up to 5 seconds
app.thread.waitAll(tids, timeout?)
Waits for all coroutines to complete.
Parameters:
tids(table) - Array of coroutine IDstimeout(number, optional) - Timeout in milliseconds
Returns: table - Array of results (corresponding to input order)
local tids = {}
for i, file in ipairs(files) do
tids[i] = app.thread.create(function()
return processFile(file)
end)
end
local results = app.thread.waitAll(tids)
app.thread.waitAny(tids, timeout?)
Waits for any one coroutine to complete.
Parameters:
tids(table) - Array of coroutine IDstimeout(number, optional) - Timeout in milliseconds
Returns: table - {tid = completed coroutine ID, result = return value}
local tid1 = app.thread.create(function() return fetchFromServer1() end)
local tid2 = app.thread.create(function() return fetchFromServer2() end)
-- Use the result from whichever returns first
local first = app.thread.waitAny({tid1, tid2})
app.log.info("Coroutine " .. first.tid .. " finished first")
app.thread.cancel(tid)
Cancels a coroutine.
Parameters:
tid(number) - Coroutine ID
Returns: boolean - Whether the cancellation was successful
local tid = app.thread.create(function()
-- Long-running task
end)
-- Cancel after timeout
app.thread.sleep(5000)
if thread.status(tid) == "running" then
app.thread.cancel(tid)
end
Note: Cancellation only takes effect when the coroutine is suspended. I/O operations in progress (e.g., shell commands) cannot be cancelled mid-execution.
app.thread.cancelAll()
Cancels all coroutines.
-- Stop all background tasks when user clicks cancel
app.thread.cancelAll()
thread.status(tid)
Gets the coroutine status.
Parameters:
tid(number) - Coroutine ID
Returns: string - Status
| Status | Description |
|---|---|
pending |
Waiting to execute |
running |
Executing |
suspended |
Suspended (waiting for I/O) |
completed |
Completed |
failed |
Execution failed |
cancelled |
Cancelled |
local status = thread.status(tid)
if status == "completed" then
local result = app.thread.wait(tid)
end
app.thread.sleep(ms)
Pauses the current coroutine execution.
Parameters:
ms(number) - Pause duration in milliseconds
app.thread.create(function()
app.log.info("Start")
app.thread.sleep(2000) -- Pause for 2 seconds
app.log.info("Continue")
end)
thread.activeCount()
Gets the number of currently active coroutines.
Returns: number
local count = thread.activeCount()
app.log.info("Active coroutines: " .. count)
Examples
Parallel File Processing
function MyPlugin:handleBatchProcess(context)
local files = context.selectedFiles
app.progress.show("Processing", {total = #files})
-- Create all coroutines
local tids = {}
for i, file in ipairs(files) do
tids[i] = app.thread.create(function()
return self:processFile(file)
end)
end
-- Wait for all to complete
local results = app.thread.waitAll(tids)
app.progress.hide()
-- Tally results
local success = 0
for _, r in ipairs(results) do
if r then success = success + 1 end
end
app.notification.show("Done", "Successfully processed " .. success .. "/" .. #files .. " files")
end
Network Request with Timeout
function MyPlugin:fetchWithTimeout(url, timeout)
local tid = app.thread.create(function()
return app.http.get(url)
end)
local result = app.thread.wait(tid, timeout or 30000)
if thread.status(tid) ~= "completed" then
app.thread.cancel(tid)
return nil, "Request timed out"
end
return result
end
Race Mode (Use the Fastest Result)
function MyPlugin:fetchFromMirrors(mirrors)
local tids = {}
for i, url in ipairs(mirrors) do
tids[i] = app.thread.create(function()
return app.http.get(url)
end)
end
-- Use the result from whichever returns first
local first = app.thread.waitAny(tids)
-- Cancel the other requests
for _, tid in ipairs(tids) do
if thread.status(tid) == "running" then
app.thread.cancel(tid)
end
end
return first.result
end
Notes
- Use cases: Coroutines are suited for I/O-intensive tasks (shell commands, HTTP requests, file operations), not for CPU-intensive computation
- Concurrency count: It is recommended to run no more than 8 coroutines simultaneously to avoid resource contention
- Cancellation limitations: I/O operations in progress cannot be cancelled immediately; the cancellation status can only be detected after the operation completes
- Error handling: Errors within a coroutine will set its status to
failed; callingapp.thread.wait()will returnnil