基于 Rx 和 curry 的延迟调用队列在 Lua 中的实现

基于 Rx 和 curry 的延迟调用队列在 Lua 中的实现

简介

本文是基于函数式编程 curry 概念的 Lua 函数调用暂存机制 的泛化包装,提高了通用性。

此实现利用了 ReplaySubjectsubscribe 时会把所有消息重放的特性。

主要用法:

-- 引用库
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

你可能感兴趣的:(Lua,游戏开发)