Last active
February 14, 2021 13:43
-
-
Save takase1121/7d7328e6a93136a49506a803c5687550 to your computer and use it in GitHub Desktop.
Minimal-ish JS Promise implementation in lua
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--[[ | |
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