5e0d58cfad
EN: 1.2.0.60 CN: 1.2.0.61 JP: 1.2.0.63 KR: 1.2.0.67
204 lines
4.2 KiB
Lua
204 lines
4.2 KiB
Lua
local M = {}
|
|
local deferred = {}
|
|
deferred.__index = deferred
|
|
local PENDING = 0
|
|
local RESOLVING = 1
|
|
local REJECTING = 2
|
|
local RESOLVED = 3
|
|
local REJECTED = 4
|
|
local finish = function(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 isfunction = function(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 promise = function(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, 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 resolve = function(deferred, state, value)
|
|
if deferred.state == 0 then
|
|
deferred.value = value
|
|
deferred.state = state
|
|
fire(deferred)
|
|
end
|
|
return deferred
|
|
end
|
|
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 synchronizer = function(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
|
|
return M
|