Skip to content

Instantly share code, notes, and snippets.

@takase1121
Last active February 14, 2021 13:43
Show Gist options
  • Save takase1121/7d7328e6a93136a49506a803c5687550 to your computer and use it in GitHub Desktop.
Save takase1121/7d7328e6a93136a49506a803c5687550 to your computer and use it in GitHub Desktop.
Minimal-ish JS Promise implementation in lua
--[[
Promises fully contained in a function.
This function returns a promise where you can resolve/reject just like in JS.
Example use case:
---
local promise = make_promise()
http.get(url, function(data) promise:resolve(data) end)
promise
:next(function(data) return json.parse(data) end)
:catch(function(err) print(err) end)
---
]]
local function make_promise(status, queue, err, rejector)
local COMPACT_THRESHOLD = 50
local STATUS = {
PENDING = 0,
RESOLVED = 1,
REJECTED = 2
}
local obj = { status = status or STATUS.PENDING, queue = queue or { n = 1 }, err = err, rejector = rejector }
function obj:try_compact_queue()
if #self.queue > COMPACT_THRESHOLD then -- configurable
local n, j = #self.queue, 1
for i = self.queue.n, n do
if i ~= j then
self.queue[j] = self.queue[i]
self.queue[i] = nil
end
j = j + 1
end
end
end
function obj:exec()
local proc = self.queue[self.queue.n]
self.queue.n = self.queue.n + 1
self:try_compact_queue() -- this is added for fun, can be omitted but may be useful
local errdata
local function on_error(msg)
errdata = msg .. "\n" .. debug.traceback(nil, 2)
end
local result = { xpcall(proc, on_error, table.unpack(self.resdata)) }
if not result[1] then -- call rejector
self.status = STATUS.REJECTED
self.rejdata = { errdata }
else
self.status = STATUS.RESOLVED
self.resdata = {table.unpack(result, 2)}
end
end
function obj:run_next()
if self.queue.n <= #self.queue and self.status == STATUS.RESOLVED then
self:exec()
return self:run_next()
elseif self.status == STATUS.REJECTED and self.rejector then
self.rejector(table.unpack(self.rejdata))
end
end
function obj:next(fn)
table.insert(self.queue, fn)
local new = make_promise(self.status, self.queue, self.err, self.rejector)
new:run_next()
return new
end
function obj:catch(fn)
self.rejector = fn
local new = make_promise(self.status, self.queue, self.err, self.rejector)
new:run_next()
return new
end
function obj:resolve(...)
if self.status == STATUS.PENDING then
self.status = STATUS.RESOLVED
self.resdata = {...}
self:run_next()
end
return self
end
function obj:reject(...)
if self.status == STATUS.PENDING then
self.status = STATUS.REJECTED
self.rejdata = {...}
self:run_next()
end
return self
end
return obj
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment