diff options
author | Matthew Wild <mwild1@gmail.com> | 2018-10-25 14:38:00 +0100 |
---|---|---|
committer | Matthew Wild <mwild1@gmail.com> | 2018-10-25 14:38:00 +0100 |
commit | e7a26ebc24cc8e22c84108ec8c6e94a1f1f1bc63 (patch) | |
tree | 1fa523ee426ab08664929087982e08ddc961d210 | |
parent | ef2ab2370018831241e4e8a42b29e1d55ad9c16f (diff) | |
download | prosody-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.lua | 37 | ||||
-rw-r--r-- | util/promise.lua | 45 |
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 |