相比V1版本這邊做了以下修改:
1) 函式命名儘量與js版保持一致,js中的then在這邊叫Next(因為then是lua的關鍵字)
2) m_DoNextObj這邊變成了一個列表,這樣改動的結果就是:之前物件間會組成單向連結串列;這邊是一個單向的樹。
3) m_DoNextObj.run函式這邊改成了m_OnFulfilled和m_OnRejected函式(為了與js保持一致)
4) SetFinished函式改成了FulFulled和Reject函式
5) V1版本,Promise物件會作為引數傳入run函式,需要手動呼叫SetFinished;這邊全部封裝在Promise內部自動完成;
Promise.lua
---@class Promise local Promise = {} Promise.__cname = "Promise" Promise.__index = Promise local State = { Pending = 0, FulFilled = 1, Rejected = -1, } ------------------ 內部函式 ------------------ ---@type Promise local Inner = {} local function OnFulFilled_Default(x) return x end local function OnRejected_Default(x) error(x) end local function IsCallbackResultPromise(result) if "table" == type(result) then local result = result.isPromise or "Promise" == result.__cname return result end return false end ---@param prmNode Promise ---@param otherPrm Promise function Inner.WaitOtherPromise(prmNode, otherPrm) if otherPrm:IsPending() then --[[ --這樣會多建立一個臨時Promise物件, 所以這邊用下面的方式 local tempPrm = otherPrm:Next(function(value) prmNode:FulFilled(value) end, function(reason) prmNode:Reject(reason) end) ]] --otherPrm作為子樹的新Root prmNode.m_TransitStateFlag = true table.insert(otherPrm.m_NextList, prmNode) else Inner.TransitionState(prmNode, otherPrm.m_State, otherPrm.m_Value) end end function Inner:NextRun() local nextList = self.m_NextList local isFulFilled = (self.m_State == State.FulFilled) for i, v in ipairs(nextList) do local nextNode = nextList[i] if true == nextNode.m_TransitStateFlag then --callback被呼叫過了 Inner.TransitionState(nextNode, self.m_State, self.m_Value) else local isSucc, result, promiseFlag if isFulFilled then isSucc, result, promiseFlag = pcall(nextNode.m_OnFulFilled, self.m_Value) else isSucc, result, promiseFlag = pcall(nextNode.m_OnRejected, self.m_Value) end if isSucc then if promiseFlag or IsCallbackResultPromise(result) then Inner.WaitOtherPromise(nextNode, result) else nextNode:FulFilled(result) end else print(debug.traceback(result, 1)) end end end for i=#nextList,1,-1 do nextList[i] = nil end end function Inner:TransitionState(state, value) if self.m_State == state or self.m_State ~= State.Pending then return end self.m_State = state self.m_Value = value Inner.NextRun(self) end ------------------ function Promise.new() local inst = {} setmetatable(inst, Promise) inst:ctor() return inst end function Promise:ctor() self.m_State = State.Pending self.m_Value = nil ---@type Promise[] self.m_NextList = {} self.m_OnFulFilled = OnFulFilled_Default self.m_OnRejected = OnRejected_Default self.m_TransitStateFlag = nil end ---@param onFulFilled fun(value):any ---@param onRejected fun(reason):any ---@return Promise function Promise:Next(onFulFilled, onRejected) local prmNode = Promise.new() prmNode.m_OnFulFilled = onFulFilled or OnFulFilled_Default prmNode.m_OnRejected = onRejected or OnRejected_Default table.insert(self.m_NextList, prmNode) if self.m_State ~= State.Pending then Inner.NextRun(self) end return prmNode end function Promise:FulFilled(result) if self.m_State == State.Pending then self.m_State = State.FulFilled self.m_Value = result Inner.NextRun(self) end end function Promise:Reject(reason) if self.m_State == State.Pending then self.m_State = State.Rejected self.m_Value = reason Inner.NextRun(self) end end ---@return boolean, boolean isPending, isFulFilled function Promise:GetState() local isPending = self.m_State == State.Pending local isFulFilled = self.m_State == State.FulFilled return isPending, isFulFilled end ---@return boolean function Promise:IsPending() return self.m_State == State.Pending end function Promise:GetValue() return self.m_Value end return Promise
一些使用例子
先載入A,再載入B,再載入C
local textList = {} local resAPrm = LoadResAsync("ResA") local prm1 = resAPrm:Next(function(textA) print("ResA load finish") table.insert(textList, textA) --a載入完, 載入b local resBPrm = LoadResAsync("ResB") return resBPrm end) local prm2 = prm1:Next(function(textB) --prm2收到的是resBPrm的值 print("ResB load finish") table.insert(textList, textB) --b載入完, 載入c local resCPrm = LoadResAsync("ResC") return resCPrm end) local lastPrm = prm2:Next(function(textC) --lastPrm收到的是resCPrm的值 table.insert(textList, textC) print(unpack(textList)) end)
promise執行鏈分析:
關於LoadResAsync
---@type MsgLoop local MsgLoop = require("MsgLoop") local _MsgLoop = MsgLoop.new(60) local function LoadResAsync(res) local connector = Promise.new() --假裝載入資源 _MsgLoop:PostRun(function(data) connector:FulFilled(data) end, 2, res) return connector end --邏輯程式碼寫在StartLoop前 require("test.lua") _MsgLoop:StartLoop()
MsgLoop參考這邊:訊息迴圈