Hello! I was recently inspired by promise concept, and I decided to tackle the implemetation of it in lua. The idea behind it is quite simple. When you perform many callbacks asynchronously , and you have issues in making them execute in particular order, this might be useful to you
I used a little tweaked version of class library :
Promise library:
Promise library:
To explain it the easiest way is by example so I brought one with me.
I used a little tweaked version of class library :
Promise library:
Code:
function createClass(parent)
local newClass = {}
newClass.overloads = {}
function newClass:new(instance)
local instance = instance or {}
newClass.overloads.__index = newClass
setmetatable(instance, newClass.overloads)
return instance
end
setmetatable(newClass, {
__call = function(t, ...)
local instance = newClass:new()
if instance.initialize ~= nil then
instance:initialize(...)
end
return instance
end
})
if(parent ~= nil) then
local _mt = getmetatable(newClass)
_mt.__index = parent
end
function newClass:getSelf()
return newClass
end
function newClass:getParent()
return baseClass
end
function newClass:isa(class)
local tmp = newClass
while(tmp ~= nil) do
if(tmp == class) then
return true
end
tmp = tmp:getParent()
end
return false
end
function newClass:setAttributes(attributes)
for k, v in pairs(attributes) do
newClass[k] = v
end
end
return newClass
end
function overloadAdd(class, callback)
class.overloads.__add = callback
end
function overloadSub(class, callback)
class.overloads.__sub = callback
end
function overloadMul(class, callback)
class.overloads.__mul = callback
end
function overloadDiv(class, callback)
class.overloads.__div = callback
end
function overloadPairs(class, callback)
class.overloads.__pairs = callback
end
function overloadIPairs(class, callback)
class.overloads.__ipairs = callback
end
function overloadToString(class, callback)
class.overloads.__tostring = callback
end
Debugger = {}
function Debugger:new(name, enabled)
local obj = {}
self.__index = self
self.__call = function(t, pr)
return t:err(pr)
end
obj.name = name
obj.enabled = enabled
setmetatable(obj,self)
return obj
end
function Debugger:err(t)
if self.enabled then print("["..tostring(self.name).."] "..tostring(t)) end
end
Promise library:
Code:
Promise = createClass()
Defer = createClass()
STATE_RESOLVED = 1
STATE_REJECTED = 2
STATE_PENDING = 3
function Promise:initialize()
self.isPromise = true
self.state = STATE_PENDING
end
function Promise:trigger()
if self.state == STATE_RESOLVED then
self.next.parameters = self.promise.data
elseif state == STATE_REJECTED then
self.next.parameters = table.pack(self.fcb())
end
end
function Promise:next(cb)
self.cb = cb
self.next = Defer()
self.next.prev = self
if self.coroutine ~= nil then
local done, err = coroutine.resume(self.coroutine)
if not done then
print("COROUTINE ERROR")
print(debug.traceback(self.coroutine, err))
end
end
return self.next.promise
end
function Promise:catch(ecb)
self.prev.ecb = ecb
end
function Promise:finnaly(fcb)
self.prev.fcb = fcb
end
function Promise:setState(s)
self.state = s
end
function Promise:getState(s)
return self.state
end
function Defer:initialize()
self.promise = Promise()
end
function Defer:resolve(...)
local promiseArg = table.pack(...)
self.promise.coroutine = coroutine.create(function()
if self.promise:getState() == STATE_PENDING then
self.promise:setState(STATE_RESOLVED)
--Resolved before promise has been chained
while self.promise.cb == null or (self.prev ~= nil and self.prev:getState() == STATE_PENDING) do
coroutine.yield()
end
local passData = self.promise.cb(table.unpack(promiseArg))
if self.promise.onResult ~= nil then
self.promise.onResult(passData)
end
local function resolveNext(data)
if self.promise.next then
self.promise.next:resolve(data)
end
end
if type(passData)=='table' and passData.isPromise then
passData:next(function(data) resolveNext(data) end)
else
resolveNext(passData)
end
end
end)
local done, err = coroutine.resume(self.promise.coroutine)
if not done then
print("COROUTINE ERROR")
print(debug.traceback(self.promise.coroutine, err))
end
end
function Defer:reject(...)
local promiseArg = table.pack(...)
self.promise.coroutine = coroutine.create(function()
if self.promise:getState() == STATE_PENDING then
self.promise:setState(STATE_REJECTED)
--Resolved before promise has been chained
if self.fcb == null then
coroutine.yield()
end
self.promise.cb(table.unpack(arg))
end
end)
end
function waitForAll(...)
local defer = Defer()
defer:resolve()
local promise = defer.promise
local agregateResults = {}
for p, promiseIter in ipairs(table.pack(...)) do
promise = promise:next(function(...)
return promiseIter
end)
promise.onResult = (function(result)
table.insert(agregateResults, result)
end)
end
return promise:next(function()
return agregateResults
end)
end
Code:
function doActionOne()
local deferred = Defer()
addEvent(function()
local data = "data1"
--Now defer is resolved
deferred.resolve(data)
end, 3000)
return deferred.promise
end
function doActionTwo(data)
local deferred = Defer()
addEvent(function()
local data = data .. "data2"
--Now defer is resolved
deferred:resolve(data)
end, 2000)
return deferred.promise
end
--Agregates promises, and chain a function which executes only if all passed promises will be resolved
waitForAll(doActionOne(), doActionTwo())
:next(function()
doCreatureSay(cid, "All previous defers were resolved, you can make some action, that means this function will be executed only if both promises from action one and action2 are resolved")
end)
Last edited: