diff options
Diffstat (limited to 'promises.lua')
-rw-r--r-- | promises.lua | 114 |
1 files changed, 114 insertions, 0 deletions
diff --git a/promises.lua b/promises.lua new file mode 100644 index 0000000..fb86af7 --- /dev/null +++ b/promises.lua @@ -0,0 +1,114 @@ +local PromisePrototype = {} + +function PromisePrototype:__run_handler(func, ...) + local values = {pcall(func, ...)} + + if table.remove(values, 1) then + self:__resolve_raw(unpack(values)) + else + self:__reject_raw(values[1]) + end +end + +function PromisePrototype:__add_child(promise) + if self.state == "resolved" then + promise:__resolve(unpack(self.values)) + elseif self.state == "rejected" then + promise:__reject(self.reason) + else + table.insert(self.__children, promise) + end +end + +function PromisePrototype:__resolve_raw(...) + self.state = "resolved" + self.values = {...} + self.reason = nil + + for _, child in ipairs(self.__children) do + child:resolve(...) + end +end + +function PromisePrototype:__reject_raw(reason) + self.state = "rejected" + self.values = nil + self.reason = reason + + local any_child = false + + for _, child in ipairs(self.__children) do + child:reject(reason) + end + + assert(any_child, "Uncaught (in promise): " .. reason) +end + +function PromisePrototype:resolve(...) + assert(self.state == "pending") + + if self.__on_resolve then + self:__run_handler(self.__on_resolve, ...) + else + self:__resolve_raw(...) + end +end + +function PromisePrototype:reject(reason) + assert(self.state == "pending") + + if self.__on_reject then + self:__run_handler(self.__on_reject, reason) + else + self:__reject_raw(reason) + end +end + +function PromisePrototype:then_(func) + local promise = Promise() + promise.__on_resolve = func + + self:__add_child(promise) + + return promise +end + +function PromisePrototype:catch(func) + local promise = Promise(function() end) + promise.__on_reject = func + + self:__add_child(promise) + + return promise +end + +Promise = setmetatable({}, { + __call = function(_, resolver) + local promise = { + state = "pending", + __children = {}, + } + + setmetatable(promise, {__index = PromisePrototype}) + + if resolver then + resolver( + function(...) + promise:resolve(...) + end, + function(...) + promise:reject(...) + end + ) + end + + return promise + end +}) + +function Promise.resolve(...) + local args = {...} + return Promise(function(resolve) + resolve(unpack(args)) + end) +end |