lua版promise实现2 - 从异步回调多层嵌套开始

异步回调代码,很容易就写出下面这样的不断嵌套的代码。如果还夹杂着各种逻辑的话,可读性会很差,还容易出错。

先加载资源A,加载完A再加载资源B,加载完B再加载资源C。

AsyncLoadRes("ResA", function(textA)
    print("ResA load finish")
    AsyncLoadRes("ResB", function(textB)
        print("ResB load finish")
        AsyncLoadRes("ResC", function(textC)
            print("ResC load finish")
            print(textA, textB, textC)
        end)
    end)
end)

 

接下来把上面的多层嵌套优化下

回调内不直接写逻辑,而是调用对象的doNext函数

local obj1 = { value = nil, doNext = nil }
AsyncLoadRes("ResA", function(textA)
    obj1.value = textA
    obj1.doNext(textA)
obj1.doNext = nil
end) local obj2 = { value = nil, doNext = nil } obj1.doNext = function(textA) print("ResA load finish") AsyncLoadRes("ResB", function(textB) obj2.value = textB obj2.doNext(textB)
obj2.doNext = nil
end) end local obj3 = { value = nil, doNext = nil } obj2.doNext = function(textB) print("ResB load finish") AsyncLoadRes("ResC", function(textC) obj3.value = textC obj3.doNext(textC)
obj3.doNext = nil
end) end obj3.doNext = function(textC) print("ResC load finish, all loaded") end

 

再把上面的代码调整下

doNext改成了含有run方法的对象,objX.doNext(xxx)改成objX.doNextObj.run(textA)

local obj1 = { value = nil, doNextObj = nil }
AsyncLoadRes("ResA", function(textA)
    obj1.value = textA
    obj1.doNextObj.run(textA, obj1.doNextObj)
obj1.doNextObj = nil
end) local obj2 = { value = nil, doNextObj = nil } obj2.run = function(textA, _obj2) print("ResA load finish") AsyncLoadRes("ResB", function(textB) obj2.value = textB obj2.doNextObj.run(textB, obj2.doNextObj)
obj2.doNextObj = nil
end) end obj1.doNextObj = obj2 local obj3 = { value = nil, doNextObj = nil } obj3.run = function(textB, _obj3) print("ResB load finish") AsyncLoadRes("ResC", function(textC) obj3.value = textC obj3.doNextObj.run(textC, obj3.doNextObj)
obj3.doNextObj = nil
end) end obj2.doNextObj = obj3 local obj4 = { value = nil, doNextObj = nil } obj4.run = function(textC, _obj4) print("ResC load finish, all loaded") end obj3.doNextObj = obj4

 为什么要这么改?

因为这样改之后,obj之间就组成了一个单向链表,这样比单纯用函数会灵活很多。

 

 

对上面的代码稍微封装下,就可以得到第1版的promise

---@class PromiseV1
local PromiseV1 = {}
PromiseV1.__cname = "Promise"
PromiseV1.__index = PromiseV1

function PromiseV1.new()
    local inst = {}
    setmetatable(inst, PromiseV1)
    inst:ctor()
    return inst
end

function PromiseV1:ctor()
    self.m_Value = nil
    self.m_IsFinish = false

    self.m_DoNextObj = nil
    self.m_Run = nil
end

function PromiseV1:Next(run)
    local obj = PromiseV1.new()
    obj.m_Run = run
    self.m_DoNextObj = obj
    return obj
 end

function PromiseV1:SetFinished(v)
    if not self.m_IsFinish then
        self.m_IsFinish = true
        self.m_Value = v
        if self.m_DoNextObj then
            self.m_DoNextObj.m_Run(v, self.m_DoNextObj)
            self.m_DoNextObj = nil
        end
    end
end

 

用法

local obj1 = PromiseV1.new()
AsyncLoadRes("ResA", function(textA)
    obj1:SetFinished(textA)
end)

local loadResB = function(textA, _obj2)
    print("ResA load finish")
    AsyncLoadRes("ResB", function(textB)
        _obj2:SetFinished(textB)
    end)
end
local obj2 = obj1:Next(loadResB)

local loadResC = function(textB, _obj3)
    print("ResB load finish")
    AsyncLoadRes("ResC", function(textC)
        _obj3:SetFinished(textC)
    end)
end
local obj3 = obj2:Next(loadResC)

local printContent = function(textC, _obj4)
    print("ResC load finish, all loaded")
end
local obj4 = obj3:Next(printContent)

 

posted @ 2024-08-14 23:32  yanghui01  阅读(3)  评论(0编辑  收藏  举报