定义:“延迟”对象,游戏中需要异步或者同步操作中不能立即得到结果,需要预定义一个结束的回调函数,等待结果后执行
回调接口函数:resolve(callback) reject(callback) catch(callback) finally(callback)
使用场景:异步向服务器请求游戏数据,从ab包中动态加载一个预制 等
local 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
if not ok and M.log ~= nil then
M.log("promise fail=", err)
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
local func, param = deferred.success, deferred.value
ok, v = xpcall(function() return func(param) end, traceback)
elseif deferred.state == REJECTING and isfunction(deferred.failure) then
local func, param = deferred.failure, deferred.value
ok, v = xpcall(function() return func(param) end, traceback)
if ok then
deferred.state = RESOLVING
end
end
if ok ~= nil then
if ok then
deferred.value = v
else
if M.log ~= nil then
M.log("promise fire fail=", v)
end
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
-- 扩展catch
function deferred:catch(failure)
return self:next(nil, failure)
end
-- 扩展finally
function deferred:finally(success)
return self:next(success, success)
end
-- 是否失败
function deferred:isRejected()
return self.state == REJECTING or self.state == REJECTED
end
-- 是否成功
function deferred:isResolved()
return self.state == RESOLVING or self.state == RESOLVED
end
-- 是否结束(不管成功与否)
function deferred:isDone()
return self.state ~= PENDING
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 = PENDING,
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_settled(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] = { resolved = resolved, value = value }
pending = pending - 1
if pending == 0 then
d:resolve(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
function M.all(args)
local d = M.new()
if #args == 0 then
return d:resolve({})
end
local pending = #args
local results = {}
for i = 1, pending do
args[i]:next(
function(value)
results[i] = value
pending = pending - 1
if pending == 0 then
d:resolve(results)
end
return value
end
, function(err)
d:reject(err)
end)
end
return d
end
return M