lua deferred延迟对象

定义:“延迟”对象,游戏中需要异步或者同步操作中不能立即得到结果,需要预定义一个结束的回调函数,等待结果后执行
回调接口函数: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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cchoop

有用的话请杯肥宅水

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值