221 lines
5.3 KiB
Lua
221 lines
5.3 KiB
Lua
--
|
|
-- lua-promises : https://github.com/zserge/lua-promises
|
|
--
|
|
|
|
local M = {}
|
|
_G.promise = M
|
|
|
|
local deferred = {}
|
|
deferred.__index = deferred
|
|
|
|
local PENDING = 0
|
|
local RESOLVING = 1
|
|
local REJECTING = 2
|
|
local RESOLVED = 3
|
|
local REJECTED = 4
|
|
|
|
local function finish(deferred, state)
|
|
state = state or REJECTED
|
|
for i, f in ipairs(deferred.queue) do
|
|
if state == RESOLVED then
|
|
f:resolve(deferred.value)
|
|
else
|
|
f:reject(deferred.value)
|
|
end
|
|
end
|
|
deferred.state = state
|
|
end
|
|
|
|
local function isfunction(f)
|
|
if type(f) == 'table' then
|
|
local mt = getmetatable(f)
|
|
return mt ~= nil and type(mt.__call) == 'function'
|
|
end
|
|
return type(f) == 'function'
|
|
end
|
|
|
|
local function promise(deferred, next, success, failure, nonpromisecb)
|
|
if type(deferred) == 'table' and type(deferred.value) == 'table' and isfunction(next) then
|
|
local called = false
|
|
local ok, err = pcall(next, deferred.value, function(v)
|
|
if called then return end
|
|
called = true
|
|
deferred.value = v
|
|
success()
|
|
end, function(v)
|
|
if called then return end
|
|
called = true
|
|
deferred.value = v
|
|
failure()
|
|
end)
|
|
if not ok and not called then
|
|
deferred.value = err
|
|
failure()
|
|
end
|
|
else
|
|
nonpromisecb()
|
|
end
|
|
end
|
|
|
|
local function fire(deferred)
|
|
local next
|
|
if type(deferred.value) == 'table' then
|
|
next = deferred.value.next
|
|
end
|
|
promise(deferred, next, function()
|
|
deferred.state = RESOLVING
|
|
fire(deferred)
|
|
end, function()
|
|
deferred.state = REJECTING
|
|
fire(deferred)
|
|
end, function()
|
|
local ok
|
|
local v
|
|
if deferred.state == RESOLVING and isfunction(deferred.success) then
|
|
ok, v = pcall(deferred.success, deferred.value)
|
|
elseif deferred.state == REJECTING and isfunction(deferred.failure) then
|
|
ok, v = pcall(deferred.failure, deferred.value)
|
|
if ok then
|
|
deferred.state = RESOLVING
|
|
end
|
|
end
|
|
|
|
if ok ~= nil then
|
|
if ok then
|
|
deferred.value = v
|
|
else
|
|
deferred.value = v
|
|
return finish(deferred)
|
|
end
|
|
end
|
|
|
|
if deferred.value == deferred then
|
|
deferred.value = pcall(error, 'resolving promise with itself')
|
|
return finish(deferred)
|
|
else
|
|
promise(deferred, next, function()
|
|
finish(deferred, RESOLVED)
|
|
end, function(state)
|
|
finish(deferred, state)
|
|
end, function()
|
|
finish(deferred, deferred.state == RESOLVING and RESOLVED)
|
|
end)
|
|
end
|
|
end)
|
|
end
|
|
|
|
local function resolve(deferred, state, value)
|
|
if deferred.state == 0 then
|
|
deferred.value = value
|
|
deferred.state = state
|
|
fire(deferred)
|
|
end
|
|
return deferred
|
|
end
|
|
|
|
--
|
|
-- PUBLIC API
|
|
--
|
|
function deferred:resolve(value)
|
|
return resolve(self, RESOLVING, value)
|
|
end
|
|
|
|
function deferred:reject(value)
|
|
return resolve(self, REJECTING, value)
|
|
end
|
|
|
|
function M.new(options)
|
|
if isfunction(options) then
|
|
local d = M.new()
|
|
local ok, err = pcall(options, d)
|
|
if not ok then
|
|
d:reject(err)
|
|
end
|
|
return d
|
|
end
|
|
options = options or {}
|
|
local d
|
|
d = {
|
|
next = function(self, success, failure)
|
|
local next = M.new({success = success, failure = failure, extend = options.extend})
|
|
if d.state == RESOLVED then
|
|
next:resolve(d.value)
|
|
elseif d.state == REJECTED then
|
|
next:reject(d.value)
|
|
else
|
|
table.insert(d.queue, next)
|
|
end
|
|
return next
|
|
end,
|
|
state = 0,
|
|
queue = {},
|
|
success = options.success,
|
|
failure = options.failure,
|
|
}
|
|
d = setmetatable(d, deferred)
|
|
if isfunction(options.extend) then
|
|
options.extend(d)
|
|
end
|
|
return d
|
|
end
|
|
|
|
function M.all(args)
|
|
local d = M.new()
|
|
if #args == 0 then
|
|
return d:resolve({})
|
|
end
|
|
local method = "resolve"
|
|
local pending = #args
|
|
local results = {}
|
|
|
|
local function synchronizer(i, resolved)
|
|
return function(value)
|
|
results[i] = value
|
|
if not resolved then
|
|
method = "reject"
|
|
end
|
|
pending = pending - 1
|
|
if pending == 0 then
|
|
d[method](d, results)
|
|
end
|
|
return value
|
|
end
|
|
end
|
|
|
|
for i = 1, pending do
|
|
args[i]:next(synchronizer(i, true), synchronizer(i, false))
|
|
end
|
|
return d
|
|
end
|
|
|
|
function M.map(args, fn)
|
|
local d = M.new()
|
|
local results = {}
|
|
local function donext(i)
|
|
if i > #args then
|
|
d:resolve(results)
|
|
else
|
|
fn(args[i]):next(function(res)
|
|
table.insert(results, res)
|
|
donext(i+1)
|
|
end, function(err)
|
|
d:reject(err)
|
|
end)
|
|
end
|
|
end
|
|
donext(1)
|
|
return d
|
|
end
|
|
|
|
function M.first(args)
|
|
local d = M.new()
|
|
for _, v in ipairs(args) do
|
|
v:next(function(res)
|
|
d:resolve(res)
|
|
end, function(err)
|
|
d:reject(err)
|
|
end)
|
|
end
|
|
return d
|
|
end
|