local M = {}
local max_timeout = 30000
local copcall = package.loaded.jit and pcall or require('coxpcall').pcall
local function resume(thread, on_finish, ...)
local ret = vim.F.pack_len(coroutine.resume(thread, ...))
local stat = ret[1]
if not stat then
on_finish(ret[2] )
elseif coroutine.status(thread) == 'dead' then
on_finish(nil, unpack(ret, 2, ret.n))
else
local fn = ret[2]
local ok, err = copcall(fn, function(...)
resume(thread, on_finish, ...)
end)
if not ok then
on_finish(err)
end
end
end
function M.run(func, on_finish)
local res
resume(coroutine.create(func), function(err, ...)
res = vim.F.pack_len(err, ...)
if on_finish then
on_finish(err, ...)
end
end)
return {
wait = function(_self, timeout)
vim.wait(timeout or max_timeout, function()
return res ~= nil
end)
assert(res, 'timeout')
if res[1] then
error(res[1])
end
return unpack(res, 2, res.n)
end,
}
end
function M.await(argc, fun, ...)
assert(coroutine.running(), 'Async.await() must be called from an async function')
local args = vim.F.pack_len(...)
return coroutine.yield(function(callback)
args[argc] = assert(callback)
fun(unpack(args, 1, math.max(argc, args.n)))
end)
end
function M.join(max_jobs, funs)
if #funs == 0 then
return
end
max_jobs = math.min(max_jobs, #funs)
local remaining = { select(max_jobs + 1, unpack(funs)) }
local to_go = #funs
M.await(1, function(on_finish)
local function run_next()
to_go = to_go - 1
if to_go == 0 then
on_finish()
elseif #remaining > 0 then
local next_fun = table.remove(remaining)
M.run(next_fun, run_next)
end
end
for i = 1, max_jobs do
M.run(funs[i], run_next)
end
end)
end
return M