From fa5a4192ff16757efa1a0abdfc08ed30eabdf761 Mon Sep 17 00:00:00 2001 From: Simon Brunel Date: Thu, 31 Jan 2019 17:59:39 +0100 Subject: [PATCH] Allow undefined rejection reason While not recommended because it makes tracking errors more difficult, it's now possible to reject a promise without explicit reason, in which case, a built-in `QPromiseUndefinedException` is thrown. This is done in anticipation of handling rejection signals without argument. --- docs/qtpromise/qpromise/constructor.md | 32 +++++++++++++++++++ src/qtpromise/qpromiseerror.h | 10 ++++++ src/qtpromise/qpromiseresolver.h | 18 +++++++++++ .../qpromise/construct/tst_construct.cpp | 26 +++++++++++++++ tests/auto/qtpromise/shared/utils.h | 10 ++++++ 5 files changed, 96 insertions(+) diff --git a/docs/qtpromise/qpromise/constructor.md b/docs/qtpromise/qpromise/constructor.md index cede2a0..4935361 100644 --- a/docs/qtpromise/qpromise/constructor.md +++ b/docs/qtpromise/qpromise/constructor.md @@ -35,3 +35,35 @@ QPromise promise([](const auto& resolve, const auto& reject) { // {...} }); ``` + +**Undefined rejection reason** + +*Since: 0.5.0* + +While not recommended because it makes tracking errors more difficult, it's also +possible to reject a promise without explicit reason, in which case, a built-in +`QPromiseUndefinedException` is thrown: + +```cpp +QPromise promise([](const QPromiseResolve& resolve, const QPromiseReject& reject) { + async_method([=](bool success, int result) { + if (success) { + resolve(result); + } else { + reject(); + } + }); +}); +``` + +```cpp +// The exception can be caught explicitly +promise.fail([](const QPromiseUndefinedException&) { + // { ... } +}) + +// ... or implicitly (since undefined) +promise.fail([]() { + // { ... } +}) +``` diff --git a/src/qtpromise/qpromiseerror.h b/src/qtpromise/qpromiseerror.h index b2ab9a4..5d81897 100644 --- a/src/qtpromise/qpromiseerror.h +++ b/src/qtpromise/qpromiseerror.h @@ -20,6 +20,16 @@ public: } }; +class QPromiseUndefinedException : public QException +{ +public: + void raise() const Q_DECL_OVERRIDE { throw *this; } + QPromiseUndefinedException* clone() const Q_DECL_OVERRIDE + { + return new QPromiseUndefinedException(*this); + } +}; + // QPromiseError is provided for backward compatibility and will be // removed in the next major version: it wasn't intended to be used // directly and thus should not be part of the public API. diff --git a/src/qtpromise/qpromiseresolver.h b/src/qtpromise/qpromiseresolver.h index b8a06e2..f48b5d1 100644 --- a/src/qtpromise/qpromiseresolver.h +++ b/src/qtpromise/qpromiseresolver.h @@ -1,6 +1,8 @@ #ifndef QTPROMISE_QPROMISERESOLVER_H #define QTPROMISE_QPROMISERESOLVER_H +#include "qpromiseerror.h" + // Qt #include @@ -34,6 +36,17 @@ public: } } + void reject() + { + auto promise = m_d->promise; + if (promise) { + Q_ASSERT(promise->isPending()); + promise->m_d->reject(QtPromise::QPromiseUndefinedException()); + promise->m_d->dispatch(); + release(); + } + } + template void resolve(V&& value) { @@ -115,6 +128,11 @@ public: m_resolver.reject(std::forward(error)); } + void operator()() const + { + m_resolver.reject(); + } + private: mutable QtPromisePrivate::PromiseResolver m_resolver; }; diff --git a/tests/auto/qtpromise/qpromise/construct/tst_construct.cpp b/tests/auto/qtpromise/qpromise/construct/tst_construct.cpp index b23a287..a428a3c 100644 --- a/tests/auto/qtpromise/qpromise/construct/tst_construct.cpp +++ b/tests/auto/qtpromise/qpromise/construct/tst_construct.cpp @@ -33,6 +33,8 @@ private Q_SLOTS: void rejectSync_void(); void rejectAsync(); void rejectAsync_void(); + void rejectUndefined(); + void rejectUndefined_void(); void connectAndResolve(); void connectAndReject(); }; @@ -234,6 +236,30 @@ void tst_qpromise_construct::rejectThrowTwoArgs_void() QCOMPARE(waitForError(p, QString()), QString("foo")); } +void tst_qpromise_construct::rejectUndefined() +{ + QPromise p([](const QPromiseResolve&, const QPromiseReject& reject) { + QtPromisePrivate::qtpromise_defer([=]() { + reject(); + }); + }); + + QCOMPARE(p.isPending(), true); + QCOMPARE(waitForRejected(p), true); +} + +void tst_qpromise_construct::rejectUndefined_void() +{ + QPromise p([](const QPromiseResolve&, const QPromiseReject& reject) { + QtPromisePrivate::qtpromise_defer([=]() { + reject(); + }); + }); + + QCOMPARE(p.isPending(), true); + QCOMPARE(waitForRejected(p), true); +} + // https://github.com/simonbrunel/qtpromise/issues/6 void tst_qpromise_construct::connectAndResolve() { diff --git a/tests/auto/qtpromise/shared/utils.h b/tests/auto/qtpromise/shared/utils.h index 56efc34..954989b 100644 --- a/tests/auto/qtpromise/shared/utils.h +++ b/tests/auto/qtpromise/shared/utils.h @@ -44,4 +44,14 @@ static inline E waitForError(const QtPromise::QPromise& promise, const E& return error; } +template +static inline bool waitForRejected(const T& promise) +{ + bool result = false; + promise.tapFail([&](const E&) { + result = true; + }).wait(); + return result; +} + #endif // QTPROMISE_TESTS_AUTO_SHARED_UTILS_H