diff options
Diffstat (limited to 'util/promise.lua')
-rw-r--r-- | util/promise.lua | 89 |
1 files changed, 75 insertions, 14 deletions
diff --git a/util/promise.lua b/util/promise.lua index 75c8697b..c4e166ed 100644 --- a/util/promise.lua +++ b/util/promise.lua @@ -2,6 +2,7 @@ local promise_methods = {}; local promise_mt = { __name = "promise", __index = promise_methods }; local xpcall = require "util.xpcall".xpcall; +local unpack = table.unpack or unpack; --luacheck: ignore 113 function promise_mt:__tostring() return "promise (" .. (self._state or "invalid") .. ")"; @@ -49,6 +50,9 @@ local function promise_settle(promise, new_state, new_next, cbs, value) for _, cb in ipairs(cbs) do cb(value); end + -- No need to keep references to callbacks + promise._pending_on_fulfilled = nil; + promise._pending_on_rejected = nil; return true; end @@ -74,30 +78,84 @@ local function new_resolve_functions(p) return _resolve, _reject; end +local next_tick = function (f) + f(); +end + local function new(f) local p = setmetatable({ _state = "pending", _next = next_pending, _pending_on_fulfilled = {}, _pending_on_rejected = {} }, promise_mt); if f then - local resolve, reject = new_resolve_functions(p); - local ok, ret = xpcall(f, debug.traceback, resolve, reject); - if not ok and p._state == "pending" then - reject(ret); - end + next_tick(function() + local resolve, reject = new_resolve_functions(p); + local ok, ret = xpcall(f, debug.traceback, resolve, reject); + if not ok and p._state == "pending" then + reject(ret); + end + end); end return p; end local function all(promises) return new(function (resolve, reject) - local count, total, results = 0, #promises, {}; - for i = 1, total do - promises[i]:next(function (v) - results[i] = v; - count = count + 1; - if count == total then - resolve(results); - end - end, reject); + local settled, results, loop_finished = 0, {}, false; + local total = 0; + for k, v in pairs(promises) do + if is_promise(v) then + total = total + 1; + v:next(function (value) + results[k] = value; + settled = settled + 1; + if settled == total and loop_finished then + resolve(results); + end + end, reject); + else + results[k] = v; + end end + loop_finished = true; + if settled == total then + resolve(results); + end + end); +end + +local function all_settled(promises) + return new(function (resolve) + local settled, results, loop_finished = 0, {}, false; + local total = 0; + for k, v in pairs(promises) do + if is_promise(v) then + total = total + 1; + v:next(function (value) + results[k] = { status = "fulfilled", value = value }; + settled = settled + 1; + if settled == total and loop_finished then + resolve(results); + end + end, function (e) + results[k] = { status = "rejected", reason = e }; + settled = settled + 1; + if settled == total and loop_finished then + resolve(results); + end + end); + else + results[k] = v; + end + end + loop_finished = true; + if settled == total then + resolve(results); + end + end); +end + +local function join(handler, ...) + local promises, n = { ... }, select("#", ...); + return all(promises):next(function (results) + return handler(unpack(results, 1, n)); end); end @@ -144,9 +202,12 @@ end return { new = new; resolve = resolve; + join = join; reject = reject; all = all; + all_settled = all_settled; race = race; try = try; is_promise = is_promise; + set_nexttick = function(new_next_tick) next_tick = new_next_tick; end; } |