本文是基于函数式编程 curry 概念的 Lua 函数调用暂存机制 的泛化包装,提高了通用性。
此实现利用了 ReplaySubject
在 subscribe
时会把所有消息重放的特性。
-- 引用库
require('delayedInvocationQueue')
-- 创建对象。fn: 是否调用真正对象的判断函数, fn_obj: 返回真正要调用的对象 obj
local inst = delayedInvocationQueue.new(fn, fn_obj)
-- 保存/触发调用: 只需要直接调用 obj 上定义的方法即可。如果 fn 返回 true,会直接调用 obj 上的方法;否则将保存调用。
inst.anyCallDefinedByObj(...)
-- 触发保存的调用
inst.invokeAll()
说明:
本实现需要 Lua 实现函数式编程中的 Curry 特性 中的 fun.bind
函数,
以及 rxLua
库。
-- delayedInvocationQueue.lua
require('curry')
local rx = require('rx')
-- theSelf 是方法调用时传入的实例,即 delayedInvocationQueue,上面存有 target 信息等
local cachedCall = function (k, theSelf, ...)
local capturedParam = ...
theSelf:cacheCall(function()
print('invoking saved call:', k, ', with param:', capturedParam)
theSelf.target()[k](theSelf.target(), capturedParam) -- 这里就是存下来的调用
end)
end
local function cacheCall(self, item)
self.queue:onNext(item)
end
local function invokeAll(self)
self.queue:subscribe(function(v)
v()
end)
self.queue:onCompleted() -- 结束 ReplaySubject 流
end
--[[
-- 创建对象。fn: 是否调用真正对象的判断函数, fn_obj: 返回真正要调用的对象 obj
local inst = delayedInvocationQueue.new(fn, fn_obj)
-- 保存/触发调用: 只需要直接调用 obj 上定义的方法即可。如果 fn 返回 true,会直接调用 obj 上的方法;否则将保存调用。
inst.anyCallDefinedByObj(...)
-- 触发保存的调用
inst.invokeAll()
]]
delayedInvocationQueue = {}
function delayedInvocationQueue.new(isTargetReady, target, ...)
-- 此实现利用了 ReplaySubject 在 subscribe 时会把所有消息重放的特性
local t = setmetatable({
isTargetReady = isTargetReady,
target = target,
queue = rx.ReplaySubject.create(),
cacheCall = cacheCall,
invokeAll = invokeAll,
}, {__index = function(t, k)
if (t:isTargetReady()) then
return target()[k] -- 如果 target 已经 ready,就直接转到真正的调用了
end
return fun.bind(cachedCall, {[1] = k}) -- 这里返回一个可调用的函数,从而拿到调用时的参数
end
})
return t
end
require('delayedInvocationQueue')
-- test
msgHub = {
onMainMessageArrived = function (self, data)
self.storageStructureByMainMessage = {
someTable = { },
updateWithComplementaryMessage = function (data)
end
}
self.storageStructureByMainMessage.someTable.valueByMainMessage = data
print('MainMessage applied')
applyCachedCall()
end,
onComplementaryMessageArrived = function(self, data)
self.storageStructureByMainMessage.someTable.valueByComplementaryMessage = data
print('ComplementaryMessage applied')
end
}
msgHub.init = function (self)
self.storageStructureByMainMessage = nil
end
msgHub.mainMsgReady = function (self)
return self.storageStructureByMainMessage ~= nil
end
function simulateComplementaryMessageArrived(data)
print('complementary msg arrived')
-- msgHub:onComplementaryMessageArrived(data) -- 如果使用这条,就会报错:attempt to index a nil value (field 'storageStructureByMainMessage')
msgHubSafe:onComplementaryMessageArrived(data)
end
function simulateMainMessageArrived(data)
print('main msg arrived')
msgHub:onMainMessageArrived(data)
end
function initCachedCall()
msgHubSafe = delayedInvocationQueue.new(function()
return msgHub:mainMsgReady()
end, function()
return msgHub
end)
end
function applyCachedCall()
msgHubSafe:invokeAll()
end
initCachedCall()
simulateComplementaryMessageArrived(12)
simulateComplementaryMessageArrived(32)
simulateMainMessageArrived(0)
输出:
complementary msg arrived
complementary msg arrived
main msg arrived
MainMessage applied
invoking saved call: onComplementaryMessageArrived , with param: 12
ComplementaryMessage applied
invoking saved call: onComplementaryMessageArrived , with param: 32
ComplementaryMessage applied