99 lines
2.5 KiB
Lua
99 lines
2.5 KiB
Lua
local promise = require('promise')
|
|
local utils = require('promise-async.utils')
|
|
local compat = require('promise-async.compat')
|
|
|
|
local asyncId = {'promise-async'}
|
|
|
|
---@class Async
|
|
local Async = setmetatable({_id = asyncId}, {
|
|
__call = function(self, executor)
|
|
return self.sync(executor)
|
|
end
|
|
})
|
|
|
|
local packedId = {}
|
|
|
|
local Packed = {_id = packedId}
|
|
Packed.__index = Packed
|
|
|
|
local function wrapPacked(packed)
|
|
return setmetatable(packed, Packed)
|
|
end
|
|
|
|
local function hasPacked(o)
|
|
return type(o) == 'table' and o._id == packedId
|
|
end
|
|
|
|
local function injectENV(fn)
|
|
compat.setfenv(fn, setmetatable({
|
|
await = Async.wait,
|
|
pcall = compat.pcall,
|
|
xpcall = compat.xpcall
|
|
}, {
|
|
__index = compat.getfenv(fn)
|
|
}))
|
|
end
|
|
|
|
function Async.sync(executor)
|
|
local typ = type(executor)
|
|
local isCallable, fn = utils.getCallable(executor, typ)
|
|
assert(isCallable, 'a callable table or function expected, got ' .. typ)
|
|
injectENV(fn)
|
|
return promise:new(function(resolve, reject)
|
|
local co = coroutine.create(typ == 'function' and executor or function()
|
|
return executor()
|
|
end)
|
|
|
|
local function afterResume(status, ...)
|
|
if not status then
|
|
local reason = select(1, ...)
|
|
reject(debug.traceback(co, reason))
|
|
return
|
|
elseif coroutine.status(co) == 'dead' then
|
|
local value
|
|
local n = select('#', ...)
|
|
if n == 1 then
|
|
value = select(1, ...)
|
|
elseif n > 1 then
|
|
value = wrapPacked({...})
|
|
end
|
|
resolve(value)
|
|
return
|
|
end
|
|
local p = select(1, ...)
|
|
return p
|
|
end
|
|
|
|
local function next(err, res)
|
|
local p = afterResume(coroutine.resume(co, err, res))
|
|
if p then
|
|
p:thenCall(function(value)
|
|
next(false, value)
|
|
end, function(reason)
|
|
next(true, reason)
|
|
end)
|
|
end
|
|
end
|
|
|
|
next()
|
|
end)
|
|
end
|
|
|
|
---Export wait function to someone needs, wait function actually have injected as `await` into
|
|
---the executor of async function
|
|
---@param p Promise|any
|
|
---@return ...
|
|
function Async.wait(p)
|
|
p = promise.resolve(p)
|
|
local err, res = coroutine.yield(p)
|
|
if err then
|
|
error(res, 0)
|
|
elseif hasPacked(res) then
|
|
return compat.unpack(res)
|
|
else
|
|
return res
|
|
end
|
|
end
|
|
|
|
return Async
|