aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthew Wild <mwild1@gmail.com>2018-10-25 14:38:00 +0100
committerMatthew Wild <mwild1@gmail.com>2018-10-25 14:38:00 +0100
commite7a26ebc24cc8e22c84108ec8c6e94a1f1f1bc63 (patch)
tree1fa523ee426ab08664929087982e08ddc961d210
parentef2ab2370018831241e4e8a42b29e1d55ad9c16f (diff)
downloadprosody-e7a26ebc24cc8e22c84108ec8c6e94a1f1f1bc63.tar.gz
prosody-e7a26ebc24cc8e22c84108ec8c6e94a1f1f1bc63.zip
util.promise: Ensure chained promises always receive a value/rejection even if an intermediate promise has no handlers
-rw-r--r--spec/util_promise_spec.lua37
-rw-r--r--util/promise.lua45
2 files changed, 60 insertions, 22 deletions
diff --git a/spec/util_promise_spec.lua b/spec/util_promise_spec.lua
index c2a8ba95..a06232b1 100644
--- a/spec/util_promise_spec.lua
+++ b/spec/util_promise_spec.lua
@@ -138,6 +138,43 @@ describe("util.promise", function ()
assert.equal("ok", result);
end);
+ it("propagates errors down the chain, even when some handlers are not provided", function ()
+ local r, result;
+ local test_error = {};
+ local p = promise.new(function (resolve, reject)
+ r = resolve;
+ end);
+ local cb = spy.new(function () end);
+ local err_cb = spy.new(function (e) result = e end);
+ local p2 = p:next(function () error(test_error) end);
+ local p3 = p2:next(cb)
+ p3:catch(err_cb);
+ assert.spy(cb).was_called(0);
+ assert.spy(err_cb).was_called(0);
+ r("oh doh");
+ assert.spy(cb).was_called(0);
+ assert.spy(err_cb).was_called(1);
+ assert.equal(test_error, result);
+ end);
+
+ it("propagates values down the chain, even when some handlers are not provided", function ()
+ local r;
+ local p = promise.new(function (resolve, reject)
+ r = resolve;
+ end);
+ local cb = spy.new(function () end);
+ local err_cb = spy.new(function () end);
+ local p2 = p:next(function (v) return v; end);
+ local p3 = p2:catch(err_cb)
+ p3:next(cb);
+ assert.spy(cb).was_called(0);
+ assert.spy(err_cb).was_called(0);
+ r(1337);
+ assert.spy(cb).was_called(1);
+ assert.spy(cb).was_called_with(1337);
+ assert.spy(err_cb).was_called(0);
+ end);
+
describe("race()", function ()
it("works with fulfilled promises", function ()
local p1, p2 = promise.resolve("yep"), promise.resolve("nope");
diff --git a/util/promise.lua b/util/promise.lua
index c0176211..fa09a01c 100644
--- a/util/promise.lua
+++ b/util/promise.lua
@@ -10,17 +10,32 @@ local function is_promise(o)
return mt == promise_mt;
end
-local function next_pending(self, on_fulfilled, on_rejected)
- table.insert(self._pending_on_fulfilled, on_fulfilled);
- table.insert(self._pending_on_rejected, on_rejected);
+local function wrap_handler(f, resolve, reject, default)
+ if not f then
+ return default;
+ end
+ return function (param)
+ local ok, ret = pcall(f, param);
+ if ok then
+ resolve(ret);
+ else
+ reject(ret);
+ end
+ return true;
+ end;
+end
+
+local function next_pending(self, on_fulfilled, on_rejected, resolve, reject)
+ table.insert(self._pending_on_fulfilled, wrap_handler(on_fulfilled, resolve, reject, resolve));
+ table.insert(self._pending_on_rejected, wrap_handler(on_rejected, resolve, reject, reject));
end
-local function next_fulfilled(promise, on_fulfilled, on_rejected) -- luacheck: ignore 212/on_rejected
- on_fulfilled(promise.value);
+local function next_fulfilled(promise, on_fulfilled, on_rejected, resolve, reject) -- luacheck: ignore 212/on_rejected
+ wrap_handler(on_fulfilled, resolve, reject)(promise.value);
end
-local function next_rejected(promise, on_fulfilled, on_rejected) -- luacheck: ignore 212/on_fulfilled
- on_rejected(promise.reason);
+local function next_rejected(promise, on_fulfilled, on_rejected, resolve, reject) -- luacheck: ignore 212/on_fulfilled
+ wrap_handler(on_rejected, resolve, reject)(promise.reason);
end
local function promise_settle(promise, new_state, new_next, cbs, value)
@@ -59,17 +74,6 @@ local function new_resolve_functions(p)
return _resolve, _reject;
end
-local function wrap_handler(f, resolve, reject)
- return function (param)
- local ok, ret = pcall(f, param);
- if ok then
- resolve(ret);
- else
- reject(ret);
- end
- end;
-end
-
local function new(f)
local p = setmetatable({ _state = "pending", _next = next_pending, _pending_on_fulfilled = {}, _pending_on_rejected = {} }, promise_mt);
if f then
@@ -123,10 +127,7 @@ end
function promise_methods:next(on_fulfilled, on_rejected)
return new(function (resolve, reject) --luacheck: ignore 431/resolve 431/reject
- self:_next(
- on_fulfilled and wrap_handler(on_fulfilled, resolve, reject) or nil,
- on_rejected and wrap_handler(on_rejected, resolve, reject) or nil
- );
+ self:_next(on_fulfilled, on_rejected, resolve, reject);
end);
end