aboutsummaryrefslogtreecommitdiffstats
path: root/util/promise.lua
diff options
context:
space:
mode:
Diffstat (limited to 'util/promise.lua')
-rw-r--r--util/promise.lua89
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;
}