Promise creation from callback only (resolver)

Make sure that the promise state can only be changed by the promise producer (and not consumers) by removing the `fulfill` and `reject` methods from the instance members and introducing a new constructor accepting a resolver lambda. That also means that a promise can't anymore be default constructed.

Add the static `QPromise<T>::resolve` and `QPromise<T>::reject` methods to create synchronously fulfilled or rejected promises, and fix the `qPromise` helper to handle deduced promises (e.g. `qPromise(QFuture<int>()) -> QPromise<int>`).
This commit is contained in:
Simon Brunel 2017-05-20 09:40:42 +02:00
parent 6a642446df
commit ce3ed72dd4
9 changed files with 677 additions and 489 deletions

View File

@ -1,25 +1,21 @@
#ifndef _QTPROMISE_QPROMISE_H
#define _QTPROMISE_QPROMISE_H
// QPromise
// QtPromise
#include "qpromise_p.h"
#include "qpromiseglobal.h"
// Qt
#include <QExplicitlySharedDataPointer>
#include <QVariant>
namespace QtPromise {
using namespace QtPromisePrivate;
template <typename T>
class QPromiseBase
{
public:
using Type = T;
QPromiseBase() : m_d(new QPromiseData<T>()) { }
virtual ~QPromiseBase() { }
bool isFulfilled() const { return m_d->resolved && !m_d->rejected; }
@ -27,22 +23,34 @@ public:
bool isPending() const { return !m_d->resolved; }
template <typename TFulfilled, typename TRejected = std::nullptr_t>
inline auto then(TFulfilled fulfilled, TRejected rejected = nullptr)
-> typename QPromiseHandler<T, TFulfilled>::Promise;
inline typename QtPromisePrivate::PromiseHandler<T, TFulfilled>::Promise
then(TFulfilled fulfilled, TRejected rejected = nullptr);
template <typename TRejected>
inline auto fail(TRejected rejected)
-> typename QPromiseHandler<T, std::nullptr_t>::Promise;
template <typename TError>
inline QPromise<T> reject(const TError& error);
inline typename QtPromisePrivate::PromiseHandler<T, std::nullptr_t>::Promise
fail(TRejected rejected);
inline QPromise<T> wait() const;
protected:
QExplicitlySharedDataPointer<QPromiseData<T> > m_d;
public: // STATIC
template <typename E>
inline static QPromise<T> reject(const E& error);
virtual void notify(const typename QPromiseData<T>::HandlerList& handlers) const = 0;
protected:
friend class QPromiseResolve<T>;
friend class QPromiseReject<T>;
QExplicitlySharedDataPointer<QtPromisePrivate::PromiseData<T> > m_d;
inline QPromiseBase();
template <typename F, typename std::enable_if<QtPromisePrivate::ArgsOf<F>::count == 1, int>::type = 0>
inline QPromiseBase(F resolver);
template <typename F, typename std::enable_if<QtPromisePrivate::ArgsOf<F>::count != 1, int>::type = 0>
inline QPromiseBase(F resolver);
virtual void notify(const typename QtPromisePrivate::PromiseData<T>::HandlerList& handlers) const = 0;
inline void dispatch();
};
@ -51,42 +59,41 @@ template <typename T>
class QPromise: public QPromiseBase<T>
{
public:
QPromise() : QPromiseBase<T>() {}
template <typename F>
QPromise(F resolver): QPromiseBase<T>(resolver) { }
template <typename THandler>
inline QPromise<T> finally(THandler handler);
inline QPromise<T> fulfill(const T& value);
public: // STATIC
inline static QPromise<QVector<T> > all(const QVector<QPromise<T> >& promises);
inline static QPromise<T> resolve(const T& value);
protected:
inline void notify(const typename QPromiseData<T>::HandlerList& handlers) const Q_DECL_OVERRIDE;
inline void notify(const typename QtPromisePrivate::PromiseData<T>::HandlerList& handlers) const Q_DECL_OVERRIDE;
private:
friend class QPromiseBase<T>;
QPromise(const QPromiseBase<T>& p) : QPromiseBase<T>(p) { }
};
template <>
class QPromise<void>: public QPromiseBase<void>
{
public:
QPromise(): QPromiseBase<void>() {}
template <typename F>
QPromise(F resolver): QPromiseBase<void>(resolver) { }
template <typename THandler>
inline QPromise<void> finally(THandler handler);
inline QPromise<void> fulfill();
public: // STATIC
inline static QPromise<void> all(const QVector<QPromise<void> >& promises);
inline static QPromise<void> resolve();
protected:
inline void notify(const typename QPromiseData<void>::HandlerList& handlers) const Q_DECL_OVERRIDE;
inline void notify(const typename QtPromisePrivate::PromiseData<void>::HandlerList& handlers) const Q_DECL_OVERRIDE;
private:
friend class QPromiseBase<void>;
@ -94,13 +101,8 @@ private:
QPromise(const QPromiseBase<void>& p) : QPromiseBase<void>(p) { }
};
using QVariantPromise = QtPromise::QPromise<QVariant>;
} // namespace QtPromise
Q_DECLARE_TYPEINFO(QtPromise::QVariantPromise, Q_MOVABLE_TYPE);
Q_DECLARE_METATYPE(QtPromise::QVariantPromise)
#include "qpromise.inl"
#include "qpromise_qfuture.inl"

View File

@ -4,15 +4,91 @@
namespace QtPromise {
template <class T = void>
class QPromiseResolve
{
public:
QPromiseResolve(const QPromise<T>& p)
: m_promise(p)
{ }
void operator()(const T& value) const
{
auto p = m_promise;
if (p.isPending()) {
p.m_d->rejected = false;
p.m_d->resolved = true;
p.m_d->value = value;
p.dispatch();
}
}
private:
QPromise<T> m_promise;
};
template <>
class QPromiseResolve<void>
{
public:
QPromiseResolve(const QPromise<void>& p)
: m_promise(p)
{ }
void operator()() const
{
auto p = m_promise;
if (p.isPending()) {
p.m_d->rejected = false;
p.m_d->resolved = true;
p.dispatch();
}
}
private:
QPromise<void> m_promise;
};
template <class T = void>
class QPromiseReject
{
public:
QPromiseReject()
{ }
QPromiseReject(const QPromise<T>& p)
: m_promise(p)
{ }
void operator()(const QPromiseError& error) const
{
auto p = m_promise;
if (p.isPending()) {
p.m_d->rejected = true;
p.m_d->resolved = true;
p.m_d->error = error;
p.dispatch();
}
}
private:
QPromise<T> m_promise;
};
template <typename T>
template <typename TFulfilled, typename TRejected>
inline auto QPromiseBase<T>::then(TFulfilled fulfilled, TRejected rejected)
-> typename QPromiseHandler<T, TFulfilled>::Promise
inline typename QtPromisePrivate::PromiseHandler<T, TFulfilled>::Promise
QPromiseBase<T>::then(TFulfilled fulfilled, TRejected rejected)
{
typename QPromiseHandler<T, TFulfilled>::Promise next;
using namespace QtPromisePrivate;
using PromiseType = typename PromiseHandler<T, TFulfilled>::Promise;
m_d->handlers << QPromiseHandler<T, TFulfilled>::create(next, fulfilled);
m_d->catchers << QPromiseCatcher<T, TRejected>::create(next, rejected);
PromiseType next([=](
const QPromiseResolve<typename PromiseType::Type>& resolve,
const QPromiseReject<typename PromiseType::Type>& reject) {
m_d->handlers << PromiseHandler<T, TFulfilled>::create(fulfilled, resolve, reject);
m_d->catchers << PromiseCatcher<T, TRejected>::create(rejected, resolve, reject);
});
if (m_d->resolved) {
dispatch();
@ -23,26 +99,12 @@ inline auto QPromiseBase<T>::then(TFulfilled fulfilled, TRejected rejected)
template <typename T>
template <typename TRejected>
inline auto QPromiseBase<T>::fail(TRejected rejected)
-> typename QPromiseHandler<T, std::nullptr_t>::Promise
inline typename QtPromisePrivate::PromiseHandler<T, std::nullptr_t>::Promise
QPromiseBase<T>::fail(TRejected rejected)
{
return then(nullptr, rejected);
}
template <typename T>
template <typename TError>
inline QPromise<T> QPromiseBase<T>::reject(const TError& error)
{
if (!m_d->resolved) {
m_d->error = QtPromisePrivate::to_exception_ptr(error);
m_d->rejected = true;
m_d->resolved = true;
dispatch();
}
return *this;
}
template <typename T>
inline QPromise<T> QPromiseBase<T>::wait() const
{
@ -55,20 +117,67 @@ inline QPromise<T> QPromiseBase<T>::wait() const
return *this;
}
template <typename T>
template <typename E>
inline QPromise<T> QPromiseBase<T>::reject(const E& error)
{
return QPromise<T>([=](const QPromiseResolve<T>&) {
throw error;
});
}
template <typename T>
inline QPromiseBase<T>::QPromiseBase()
: m_d(new QtPromisePrivate::PromiseData<T>())
{
}
template <typename T>
template <typename F, typename std::enable_if<QtPromisePrivate::ArgsOf<F>::count == 1, int>::type>
inline QPromiseBase<T>::QPromiseBase(F resolver)
: m_d(new QtPromisePrivate::PromiseData<T>())
{
auto resolve = QPromiseResolve<T>(*this);
auto reject = QPromiseReject<T>(*this);
try {
resolver(resolve);
} catch(...) {
reject(std::current_exception());
}
}
template <typename T>
template <typename F, typename std::enable_if<QtPromisePrivate::ArgsOf<F>::count != 1, int>::type>
inline QPromiseBase<T>::QPromiseBase(F resolver)
: m_d(new QtPromisePrivate::PromiseData<T>())
{
auto resolve = QPromiseResolve<T>(*this);
auto reject = QPromiseReject<T>(*this);
try {
resolver(resolve, reject);
} catch(...) {
reject(std::current_exception());
}
}
template <typename T>
inline void QPromiseBase<T>::dispatch()
{
using namespace QtPromisePrivate;
Q_ASSERT(m_d->resolved);
typename QPromiseData<T>::HandlerList handlers;
typename QPromiseData<T>::CatcherList catchers;
typename PromiseData<T>::HandlerList handlers;
typename PromiseData<T>::CatcherList catchers;
handlers.swap(m_d->handlers);
catchers.swap(m_d->catchers);
if (m_d->rejected) {
const std::exception_ptr error = m_d->error;
qtpromise_defer([catchers, error]() {
const QPromiseError error = m_d->error;
qtpromise_defer([=]() {
for (auto catcher: catchers) {
catcher(error);
}
@ -83,67 +192,62 @@ template <typename THandler>
inline QPromise<T> QPromise<T>::finally(THandler handler)
{
return this->then([=](const T& res) {
return QPromise<void>().fulfill().then(handler).then([=](){
return QPromise<void>::resolve().then(handler).then([=](){
return res;
});
}, [=]() {
const auto exception = std::current_exception();
return QPromise<void>().fulfill().then(handler).then([=](){
return QPromise<void>::resolve().then(handler).then([=](){
std::rethrow_exception(exception);
return T();
});
});
}
template <typename T>
inline QPromise<T> QPromise<T>::fulfill(const T& value)
{
if (!this->m_d->resolved) {
this->m_d->rejected = false;
this->m_d->resolved = true;
this->m_d->value = value;
this->dispatch();
}
return *this;
}
template <typename T>
inline QPromise<QVector<T> > QPromise<T>::all(const QVector<QPromise<T> >& promises)
{
QPromise<QVector<T> > next;
const int count = promises.size();
if (count == 0) {
return next.fulfill({});
return QPromise<QVector<T> >::resolve({});
}
QSharedPointer<int> remaining(new int(count));
QSharedPointer<QVector<T> > results(new QVector<T>(count));
return QPromise<QVector<T> >([=](
const QPromiseResolve<QVector<T> >& resolve,
const QPromiseReject<QVector<T> >& reject) {
for (int i=0; i<count; ++i) {
QPromise<T>(promises[i]).then([=](const T& res) mutable {
if (next.isPending()) {
QSharedPointer<int> remaining(new int(count));
QSharedPointer<QVector<T> > results(new QVector<T>(count));
for (int i=0; i<count; ++i) {
QPromise<T>(promises[i]).then([=](const T& res) mutable {
(*results)[i] = res;
if (--(*remaining) == 0) {
next.fulfill(*results);
resolve(*results);
}
}
}, [=]() mutable {
if (next.isPending()) {
next.reject(std::current_exception());
}
});
}
return next;
}, [=]() mutable {
if (*remaining != -1) {
*remaining = -1;
reject(std::current_exception());
}
});
}
});
}
template <typename T>
inline void QPromise<T>::notify(const typename QPromiseData<T>::HandlerList& handlers) const
inline QPromise<T> QPromise<T>::resolve(const T& value)
{
return QPromise<T>([=](const QPromiseResolve<T>& resolve) {
resolve(value);
});
}
template <typename T>
inline void QPromise<T>::notify(const typename QtPromisePrivate::PromiseData<T>::HandlerList& handlers) const
{
const T value = this->m_d->value;
qtpromise_defer([handlers, value]() {
QtPromisePrivate::qtpromise_defer([handlers, value]() {
for (auto handler: handlers) {
handler(value);
}
@ -154,52 +258,53 @@ template <typename THandler>
inline QPromise<void> QPromise<void>::finally(THandler handler)
{
return this->then([=]() {
return QPromise<void>().fulfill().then(handler).then([](){});
return QPromise<void>::resolve().then(handler).then([](){});
}, [=]() {
const auto exception = std::current_exception();
return QPromise<void>().fulfill().then(handler).then([=](){
return QPromise<void>::resolve().then(handler).then([=](){
std::rethrow_exception(exception);
});
});
}
inline QPromise<void> QPromise<void>::fulfill()
{
if (!m_d->resolved) {
m_d->rejected = false;
m_d->resolved = true;
dispatch();
}
return *this;
}
inline QPromise<void> QPromise<void>::all(const QVector<QPromise<void> >& promises)
{
QPromise<void> next;
QSharedPointer<int> remaining(new int(promises.size()));
for (const auto& promise: promises) {
QPromise<void>(promise).then([=]() mutable {
if (next.isPending()) {
if (--(*remaining) == 0) {
next.fulfill();
}
}
}, [=]() mutable {
if (next.isPending()) {
next.reject(std::current_exception());
}
});
const int count = promises.size();
if (count == 0) {
return QPromise<void>::resolve();
}
return next;
return QPromise<void>([=](
const QPromiseResolve<void>& resolve,
const QPromiseReject<void>& reject) {
QSharedPointer<int> remaining(new int(promises.size()));
for (const auto& promise: promises) {
QPromise<void>(promise).then([=]() {
if (--(*remaining) == 0) {
resolve();
}
}, [=]() {
if (*remaining != -1) {
*remaining = -1;
reject(std::current_exception());
}
});
}
});
}
inline void QPromise<void>::notify(const typename QPromiseData<void>::HandlerList& handlers) const
inline QPromise<void> QPromise<void>::resolve()
{
qtpromise_defer([handlers]() {
return QPromise<void>([](const QPromiseResolve<void>& resolve) {
resolve();
});
}
inline void QPromise<void>::notify(const typename QtPromisePrivate::PromiseData<void>::HandlerList& handlers) const
{
QtPromisePrivate::qtpromise_defer([handlers]() {
for (const auto& handler: handlers) {
handler();
}
@ -209,9 +314,23 @@ inline void QPromise<void>::notify(const typename QPromiseData<void>::HandlerLis
// Helpers
template <typename T>
QPromise<T> qPromise(const T& value)
typename QtPromisePrivate::PromiseDeduce<T>::Type qPromise(const T& value)
{
return QPromise<T>().fulfill(value);
using namespace QtPromisePrivate;
using Promise = typename PromiseDeduce<T>::Type;
return Promise([=](
const QPromiseResolve<typename Promise::Type>& resolve,
const QPromiseReject<typename Promise::Type>& reject) {
PromiseFulfill<T>::call(value, resolve, reject);
});
}
QPromise<void> qPromise()
{
return QPromise<void>([](
const QPromiseResolve<void>& resolve) {
resolve();
});
}
template <typename T>

View File

@ -1,7 +1,8 @@
#ifndef _QTPROMISE_QPROMISE_P_H
#define _QTPROMISE_QPROMISE_P_H
// QPromise
// QtPromise
#include "qpromiseerror.h"
#include "qpromiseglobal.h"
// Qt
@ -10,18 +11,21 @@
#include <QSharedData>
#include <QVector>
// STL
#include <functional>
namespace QtPromise {
template <typename T = void>
template <typename T>
class QPromise;
}
template <typename T>
class QPromiseResolve;
template <typename T>
class QPromiseReject;
} // namespace QtPromise
namespace QtPromisePrivate {
using namespace QtPromise;
template <typename F>
inline void qtpromise_defer(F f)
{
@ -29,265 +33,269 @@ inline void qtpromise_defer(F f)
}
template <typename T>
struct QPromiseDeduce
struct PromiseDeduce
{
using Type = QPromise<Unqualified<T> >;
using Type = QtPromise::QPromise<Unqualified<T> >;
};
template <typename T>
struct QPromiseDeduce<QPromise<T> >
: public QPromiseDeduce<T>
struct PromiseDeduce<QtPromise::QPromise<T> >
: public PromiseDeduce<T>
{ };
template <typename T, typename TPromise = typename QPromiseDeduce<T>::Type>
struct QPromiseFulfill
template <typename T>
struct PromiseFulfill
{
static void call(TPromise next, const T& value)
static void call(
const T& value,
const QtPromise::QPromiseResolve<T>& resolve,
const QtPromise::QPromiseReject<T>&)
{
next.fulfill(value);
resolve(value);
}
};
template <typename T>
struct QPromiseFulfill<QPromise<T>, QPromise<T> >
struct PromiseFulfill<QtPromise::QPromise<T> >
{
static void call(QPromise<T> next, QPromise<T> promise)
static void call(
QtPromise::QPromise<T> promise,
const QtPromise::QPromiseResolve<T>& resolve,
const QtPromise::QPromiseReject<T>& reject)
{
promise.then(
[=](const T& value) mutable {
next.fulfill(value);
[=](const T& value) {
resolve(value);
},
[=]() mutable {
next.reject(std::current_exception());
[=]() { // catch all
reject(std::current_exception());
});
}
};
template <>
struct QPromiseFulfill<QPromise<void>, QPromise<void> >
struct PromiseFulfill<QtPromise::QPromise<void> >
{
template <typename TPromise>
static void call(TPromise next, TPromise promise)
template <typename TPromise, typename TResolve, typename TReject>
static void call(TPromise promise, TResolve resolve, TReject reject)
{
promise.then(
[=]() mutable {
next.fulfill();
[=]() {
resolve();
},
[=]() mutable {
next.reject(std::current_exception());
[=]() { // catch all
reject(std::current_exception());
});
}
};
template <typename T, typename TRes>
struct QPromiseDispatch
struct PromiseDispatch
{
using Promise = typename QPromiseDeduce<TRes>::Type;
using Promise = typename PromiseDeduce<TRes>::Type;
using ResType = Unqualified<TRes>;
template <typename TPromise, typename THandler>
static void call(TPromise next, THandler handler, const T& value)
template <typename THandler, typename TResolve, typename TReject>
static void call(const T& value, THandler handler, TResolve resolve, TReject reject)
{
ResType res;
try {
res = handler(value);
const ResType res = handler(value);
PromiseFulfill<ResType>::call(res, resolve, reject);
} catch (...) {
next.reject(std::current_exception());
return;
reject(std::current_exception());
}
QPromiseFulfill<ResType>::call(next, res);
}
};
template <typename T>
struct QPromiseDispatch<T, void>
struct PromiseDispatch<T, void>
{
using Promise = QPromise<void>;
using Promise = QtPromise::QPromise<void>;
template <typename TPromise, typename THandler>
static void call(TPromise next, THandler handler, const T& value)
template <typename THandler, typename TResolve, typename TReject>
static void call(const T& value, THandler handler, TResolve resolve, TReject reject)
{
try {
handler(value);
resolve();
} catch (...) {
next.reject(std::current_exception());
return;
reject(std::current_exception());
}
next.fulfill();
}
};
template <typename TRes>
struct QPromiseDispatch<void, TRes>
struct PromiseDispatch<void, TRes>
{
using Promise = typename QPromiseDeduce<TRes>::Type;
using Promise = typename PromiseDeduce<TRes>::Type;
using ResType = Unqualified<TRes>;
template <typename TPromise, typename THandler>
static void call(TPromise next, THandler handler)
template <typename THandler, typename TResolve, typename TReject>
static void call(THandler handler, TResolve resolve, TReject reject)
{
ResType res;
try {
res = handler();
const ResType res = handler();
PromiseFulfill<ResType>::call(res, resolve, reject);
} catch (...) {
next.reject(std::current_exception());
return;
reject(std::current_exception());
}
QPromiseFulfill<ResType>::call(next, res);
}
};
template <>
struct QPromiseDispatch<void, void>
struct PromiseDispatch<void, void>
{
using Promise = QPromise<void>;
using Promise = QtPromise::QPromise<void>;
template <typename TPromise, typename THandler>
static void call(TPromise next, THandler handler)
template <typename THandler, typename TResolve, typename TReject>
static void call(THandler handler, TResolve resolve, TReject reject)
{
try {
handler();
resolve();
} catch (...) {
next.reject(std::current_exception());
return;
reject(std::current_exception());
}
next.fulfill();
}
};
template <typename T, typename THandler, typename TArg = typename ArgsOf<THandler>::first>
struct QPromiseHandler
struct PromiseHandler
{
using ResType = typename std::result_of<THandler(T)>::type;
using Promise = typename QPromiseDispatch<T, ResType>::Promise;
using Promise = typename PromiseDispatch<T, ResType>::Promise;
static std::function<void(T)> create(const Promise& next, THandler handler)
template <typename TResolve, typename TReject>
static std::function<void(T)> create(THandler handler, TResolve resolve, TReject reject)
{
return [=](const T& value) mutable {
QPromiseDispatch<T, ResType>::call(next, handler, value);
return [=](const T& value) {
PromiseDispatch<T, ResType>::call(value, handler, resolve, reject);
};
}
};
template <typename T, typename THandler>
struct QPromiseHandler<T, THandler, void>
struct PromiseHandler<T, THandler, void>
{
using ResType = typename std::result_of<THandler()>::type;
using Promise = typename QPromiseDispatch<T, ResType>::Promise;
using Promise = typename PromiseDispatch<T, ResType>::Promise;
static std::function<void(T)> create(const Promise& next, THandler handler)
template <typename TResolve, typename TReject>
static std::function<void(T)> create(THandler handler, TResolve resolve, TReject reject)
{
return [=](const T&) mutable {
QPromiseDispatch<void, ResType>::call(next, handler);
return [=](const T&) {
PromiseDispatch<void, ResType>::call(handler, resolve, reject);
};
}
};
template <typename THandler>
struct QPromiseHandler<void, THandler, void>
struct PromiseHandler<void, THandler, void>
{
using ResType = typename std::result_of<THandler()>::type;
using Promise = typename QPromiseDispatch<void, ResType>::Promise;
using Promise = typename PromiseDispatch<void, ResType>::Promise;
static std::function<void()> create(const Promise& next, THandler handler)
template <typename TResolve, typename TReject>
static std::function<void()> create(THandler handler, TResolve resolve, TReject reject)
{
return [=]() {
QPromiseDispatch<void, ResType>::call(next, handler);
PromiseDispatch<void, ResType>::call(handler, resolve, reject);
};
}
};
template <typename T>
struct QPromiseHandler<T, std::nullptr_t, void>
struct PromiseHandler<T, std::nullptr_t, void>
{
using Promise = QPromise<T>;
using Promise = QtPromise::QPromise<T>;
static std::function<void(T)> create(const Promise& next, std::nullptr_t)
template <typename TResolve, typename TReject>
static std::function<void(T)> create(std::nullptr_t, TResolve resolve, TReject reject)
{
return [next](const T& value) mutable {
return [=](const T& value) {
// 2.2.7.3. If onFulfilled is not a function and promise1 is fulfilled,
// promise2 must be fulfilled with the same value as promise1.
QPromiseFulfill<T>::call(next, value);
PromiseFulfill<T>::call(value, resolve, reject);
};
}
};
template <>
struct QPromiseHandler<void, std::nullptr_t, void>
struct PromiseHandler<void, std::nullptr_t, void>
{
using Promise = QPromise<void>;
using Promise = QtPromise::QPromise<void>;
template <typename TPromise>
static std::function<void()> create(const TPromise& next, std::nullptr_t)
template <typename TResolve, typename TReject>
static std::function<void()> create(std::nullptr_t, TResolve resolve, TReject)
{
return [=]() mutable {
return [=]() {
// 2.2.7.3. If onFulfilled is not a function and promise1 is fulfilled,
// promise2 must be fulfilled with the same value as promise1.
TPromise(next).fulfill();
resolve();
};
}
};
template <typename T, typename THandler, typename TArg = typename ArgsOf<THandler>::first>
struct QPromiseCatcher
struct PromiseCatcher
{
using Type = std::function<void(std::exception_ptr)>;
using Functor = std::function<void(QtPromise::QPromiseError)>;
using ResType = typename std::result_of<THandler(TArg)>::type;
template <typename TPromise>
static Type create(const TPromise& next, THandler handler)
template <typename TResolve, typename TReject>
static Functor create(THandler handler, TResolve resolve, TReject reject)
{
return [=](const std::exception_ptr& eptr) mutable {
return [=](const QtPromise::QPromiseError& error) {
try {
std::rethrow_exception(eptr);
error.rethrow();
} catch (const TArg& error) {
QPromiseDispatch<TArg, ResType>::call(next, handler, error);
PromiseDispatch<TArg, ResType>::call(error, handler, resolve, reject);
} catch(...) {
TPromise(next).reject(std::current_exception());
reject(std::current_exception());
}
};
}
};
template <typename T, typename THandler>
struct QPromiseCatcher<T, THandler, void>
struct PromiseCatcher<T, THandler, void>
{
using Type = std::function<void(std::exception_ptr)>;
using Functor = std::function<void(QtPromise::QPromiseError)>;
using ResType = typename std::result_of<THandler()>::type;
template <typename TPromise>
static Type create(const TPromise& next, THandler handler)
template <typename TResolve, typename TReject>
static Functor create(THandler handler, TResolve resolve, TReject reject)
{
return [=](const std::exception_ptr& eptr) mutable {
return [=](const QtPromise::QPromiseError& error) {
try {
std::rethrow_exception(eptr);
error.rethrow();
} catch (...) {
QPromiseDispatch<void, ResType>::call(next, handler);
PromiseDispatch<void, ResType>::call(handler, resolve, reject);
}
};
}
};
template <typename T>
struct QPromiseCatcher<T, std::nullptr_t, void>
struct PromiseCatcher<T, std::nullptr_t, void>
{
using Type = std::function<void(std::exception_ptr)>;
using Functor = std::function<void(QtPromise::QPromiseError)>;
template <typename TPromise>
static Type create(const TPromise& next, std::nullptr_t)
template <typename TResolve, typename TReject>
static Functor create(std::nullptr_t, TResolve, TReject reject)
{
return [=](const std::exception_ptr& eptr) mutable {
return [=](const QtPromise::QPromiseError& error) {
// 2.2.7.4. If onRejected is not a function and promise1 is rejected,
// promise2 must be rejected with the same reason as promise1
TPromise(next).reject(eptr);
reject(error);
};
}
};
template <class T>
struct QPromiseDataBase: public QSharedData
struct PromiseDataBase: public QSharedData
{
using ErrorType = std::exception_ptr;
using ErrorType = QtPromise::QPromiseError;
using CatcherList = QVector<std::function<void(ErrorType)> >;
bool resolved;
@ -297,7 +305,7 @@ struct QPromiseDataBase: public QSharedData
};
template <typename T>
struct QPromiseData: QPromiseDataBase<T>
struct PromiseData: PromiseDataBase<T>
{
using HandlerList = QVector<std::function<void(T)> >;
@ -306,7 +314,7 @@ struct QPromiseData: QPromiseDataBase<T>
};
template <>
struct QPromiseData<void>: QPromiseDataBase<void>
struct PromiseData<void>: PromiseDataBase<void>
{
using HandlerList = QVector<std::function<void()> >;

View File

@ -4,76 +4,61 @@
namespace QtPromisePrivate {
template <typename T>
struct QPromiseDeduce<QFuture<T> >
: public QPromiseDeduce<T>
struct PromiseDeduce<QFuture<T> >
: public PromiseDeduce<T>
{ };
template <typename T>
struct QPromiseFulfill<QFuture<T>, QPromise<T> >
struct PromiseFulfill<QFuture<T> >
{
static void call(QPromise<T> next, const QFuture<T>& future)
static void call(
const QFuture<T>& future,
const QtPromise::QPromiseResolve<T>& resolve,
const QtPromise::QPromiseReject<T>& reject)
{
using Watcher = QFutureWatcher<T>;
Watcher* watcher = new Watcher();
QObject::connect(
watcher, &Watcher::finished,
[next, watcher]() mutable {
T res;
try {
res = watcher->result();
} catch(...) {
next.reject(std::current_exception());
}
QObject::connect(watcher, &Watcher::finished, [=]() mutable {
try {
T res = watcher->result();
PromiseFulfill<T>::call(res, resolve, reject);
} catch(...) {
reject(std::current_exception());
}
watcher->deleteLater();
if (next.isPending()) {
QPromiseFulfill<T>::call(next, res);
}
});
watcher->deleteLater();
});
watcher->setFuture(future);
}
};
template <>
struct QPromiseFulfill<QFuture<void>, QPromise<void> >
struct PromiseFulfill<QFuture<void> >
{
static void call(QPromise<void> next, const QFuture<void>& future)
static void call(
const QFuture<void>& future,
const QtPromise::QPromiseResolve<void>& resolve,
const QtPromise::QPromiseReject<void>& reject)
{
using Watcher = QFutureWatcher<void>;
Watcher* watcher = new Watcher();
QObject::connect(
watcher, &Watcher::finished,
[next, watcher]() mutable {
try {
// let's rethrown possibe exception
watcher->waitForFinished();
} catch(...) {
next.reject(std::current_exception());
}
QObject::connect(watcher, &Watcher::finished, [=]() mutable {
try {
// let's rethrown possibe exception
watcher->waitForFinished();
resolve();
} catch(...) {
reject(std::current_exception());
}
watcher->deleteLater();
if (next.isPending()) {
next.fulfill();
}
});
watcher->deleteLater();
});
watcher->setFuture(future);
}
};
} // namespace QtPromisePrivate
namespace QtPromise {
template <typename T>
QPromise<T> qPromise(const QFuture<T>& future)
{
QPromise<T> next;
QPromiseFulfill<QFuture<T> >::call(next, future);
return next;
}
} // namespace QtPromise

View File

@ -0,0 +1,42 @@
#ifndef _QTPROMISE_QPROMISEERROR_H
#define _QTPROMISE_QPROMISEERROR_H
// QtPromise
#include "qpromiseglobal.h"
namespace QtPromise {
class QPromiseError
{
public:
QPromiseError()
: exception(nullptr)
{ }
template <typename T>
QPromiseError(const T& value)
: exception(nullptr)
{
try {
throw value;
} catch(...) {
exception = std::current_exception();
}
}
QPromiseError(const std::exception_ptr& exception)
: exception(exception)
{ }
void rethrow() const
{
std::rethrow_exception(exception);
}
private:
std::exception_ptr exception;
};
} // namespace QtPromise
#endif // _QTPROMISE_QPROMISEERROR_H

View File

@ -6,6 +6,7 @@
// STL
#include <functional>
#include <array>
namespace QtPromisePrivate
{
@ -13,6 +14,22 @@ namespace QtPromisePrivate
template <typename T>
using Unqualified = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
/*!
* \struct HasCallOperator
* http://stackoverflow.com/a/5839442
*/
template <typename T>
struct HasCallOperator
{
template <class U>
static auto check(const U* u)
-> decltype(&U::operator(), char(0));
static std::array<char, 2> check(...);
static const bool value = (sizeof(check((T*)0)) == 1);
};
/*!
* \struct ArgsOf
* http://stackoverflow.com/a/7943765
@ -23,6 +40,7 @@ struct ArgsTraits
{
using types = std::tuple<Args...>;
using first = typename std::tuple_element<0, types>::type;
static const size_t count = std::tuple_size<types>::value;
};
template <>
@ -30,14 +48,16 @@ struct ArgsTraits<>
{
using types = std::tuple<>;
using first = void;
static const size_t count = 0;
};
template <typename T>
struct ArgsOf : public ArgsOf<decltype(&T::operator())>
template <typename T, typename Enable = void>
struct ArgsOf : public ArgsTraits<>
{ };
template <>
struct ArgsOf<std::nullptr_t> : public ArgsTraits<>
template <typename T>
struct ArgsOf<T, typename std::enable_if<HasCallOperator<T>::value>::type>
: public ArgsOf<decltype(&T::operator())>
{ };
template <typename TReturn, typename... Args>
@ -100,25 +120,6 @@ template <typename T>
struct ArgsOf<const volatile T&&> : public ArgsOf<T>
{ };
/*!
* \fn to_exception_ptr
*/
template <typename T>
std::exception_ptr to_exception_ptr(const T& value)
{
try {
throw value;
} catch(...) {
return std::current_exception();
}
}
template <>
std::exception_ptr to_exception_ptr(const std::exception_ptr& value)
{
return value;
}
} // namespace QtPromisePrivate
#endif // ifndef _QTPROMISE_QPROMISEGLOBAL_H

View File

@ -3,4 +3,5 @@ HEADERS += \
$$PWD/qpromise.inl \
$$PWD/qpromise_p.h \
$$PWD/qpromise_qfuture.inl \
$$PWD/qpromiseerror.h \
$$PWD/qpromiseglobal.h

View File

@ -5,15 +5,13 @@
#include <QtTest>
using namespace QtPromise;
static const int ASYNC_DELAY = 256;
using namespace QtPromisePrivate;
class tst_qpromise: public QObject
{
Q_OBJECT
private Q_SLOTS:
void finallyReturns();
void finallyThrows();
void finallyDelayedFulfilled();
@ -26,75 +24,68 @@ QTEST_MAIN(tst_qpromise)
void tst_qpromise::finallyReturns()
{
{
QPromise<int> p;
{ // fulfilled
QVector<int> values;
auto next = p.finally([&values]() {
auto next = QPromise<int>::resolve(42).finally([&]() {
values << 8;
return 16;
return 16; // ignored!
});
p.fulfill(42);
next.then([&values](int r) {
next.then([&](int r) {
values << r;
}).wait();
QVERIFY(p.isFulfilled());
QVERIFY(next.isFulfilled());
QCOMPARE(values, QVector<int>({8, 42}));
}
{
QPromise<int> p;
QVector<int> values;
auto next = p.finally([&values]() {
values << 8;
return 16;
{ // rejected
QString error;
int value = -1;
auto next = QPromise<int>([](const QPromiseResolve<int>) {
throw QString("foo");
}).finally([&]() {
value = 8;
return 16; // ignored!
});
p.reject(QString("foo"));
next.then([&values](int r) {
values << r;
next.fail([&](const QString& err) {
error = err;
return 42;
}).wait();
QVERIFY(p.isRejected());
QVERIFY(next.isRejected());
QCOMPARE(values, QVector<int>({8}));
QCOMPARE(error, QString("foo"));
QCOMPARE(value, 8);
}
}
void tst_qpromise::finallyThrows()
{
{
QPromise<int> p;
{ // fulfilled
QString error;
auto next = p.finally([]() {
auto next = QPromise<int>::resolve(42).finally([&]() {
throw QString("bar");
});
p.fulfill(42);
next.fail([&error](const QString& err) {
next.fail([&](const QString& err) {
error = err;
return 0;
}).wait();
QVERIFY(p.isFulfilled());
QVERIFY(next.isRejected());
QCOMPARE(error, QString("bar"));
}
{
QPromise<int> p;
{ // rejected
QString error;
auto next = p.finally([]() {
auto next = QPromise<int>::reject(QString("foo")).finally([&]() {
throw QString("bar");
});
p.reject(QString("foo"));
next.fail([&error](const QString& err) {
next.fail([&](const QString& err) {
error = err;
return 0;
}).wait();
QVERIFY(p.isRejected());
QVERIFY(next.isRejected());
QCOMPARE(error, QString("bar"));
}
@ -102,97 +93,89 @@ void tst_qpromise::finallyThrows()
void tst_qpromise::finallyDelayedFulfilled()
{
{
QPromise<int> p0;
{ // fulfilled
QVector<int> values;
auto next = p0.finally([&values]() {
QPromise<int> p1;
QTimer::singleShot(ASYNC_DELAY, [p1, &values]() mutable {
values << 64;
p1.fulfill(16);
auto next = QPromise<int>::resolve(42).finally([&]() {
QPromise<int> p([&](const QPromiseResolve<int>& resolve) {
qtpromise_defer([=, &values]() {
values << 64;
resolve(16); // ignored!
});
});
values << 8;
return p1;
return p;
});
p0.fulfill(42);
next.then([&values](int r) {
next.then([&](int r) {
values << r;
}).wait();
QVERIFY(p0.isFulfilled());
QVERIFY(next.isFulfilled());
QCOMPARE(values, QVector<int>({8, 64, 42}));
}
{
QPromise<int> p0;
{ // rejected
QString error;
QVector<int> values;
auto next = p0.finally([&values]() {
QPromise<int> p1;
QTimer::singleShot(ASYNC_DELAY, [p1, &values]() mutable {
values << 64;
p1.fulfill(16);
auto next = QPromise<int>::reject(QString("foo")).finally([&]() {
QPromise<int> p([&](const QPromiseResolve<int>& resolve) {
qtpromise_defer([=, &values]() {
values << 64;
resolve(16); // ignored!
});
});
values << 8;
return p1;
return p;
});
p0.reject(QString("foo"));
next.then([&values](int r) {
next.then([&](int r) {
values << r;
}, [&](const QString& err) {
error = err;
}).wait();
QVERIFY(p0.isRejected());
QVERIFY(next.isRejected());
QCOMPARE(error, QString("foo"));
QCOMPARE(values, QVector<int>({8, 64}));
}
}
void tst_qpromise::finallyDelayedRejected()
{
{
QPromise<int> p0;
{ // fulfilled
QString error;
auto next = p0.finally([]() {
QPromise<int> p1;
QTimer::singleShot(ASYNC_DELAY, [p1]() mutable {
p1.reject(QString("bar"));
auto next = QPromise<int>::resolve(42).finally([]() {
return QPromise<int>([](const QPromiseResolve<int>&, const QPromiseReject<int>& reject) {
qtpromise_defer([=]() {
reject(QString("bar"));
});
});
return p1;
});
p0.fulfill(42);
next.fail([&error](const QString& err) {
next.fail([&](const QString& err) {
error = err;
return 0;
}).wait();
QVERIFY(p0.isFulfilled());
QVERIFY(next.isRejected());
QCOMPARE(error, QString("bar"));
}
{
QPromise<int> p0;
{ // rejected
QString error;
auto next = p0.finally([]() {
QPromise<int> p1;
QTimer::singleShot(ASYNC_DELAY, [p1]() mutable {
p1.reject(QString("bar"));
auto next = QPromise<int>::reject(QString("foo")).finally([]() {
return QPromise<int>([](const QPromiseResolve<int>&, const QPromiseReject<int>& reject) {
qtpromise_defer([=]() {
reject(QString("bar"));
});
});
return p1;
});
p0.reject(QString("foo"));
next.fail([&error](const QString& err) {
next.fail([&](const QString& err) {
error = err;
return 0;
}).wait();
QVERIFY(p0.isRejected());
QVERIFY(next.isRejected());
QCOMPARE(error, QString("bar"));
}

View File

@ -5,6 +5,7 @@
#include <QtTest>
using namespace QtPromise;
using namespace QtPromisePrivate;
// https://promisesaplus.com/#requirements
class tst_requirements: public QObject
@ -37,115 +38,151 @@ QTEST_MAIN(tst_requirements)
void tst_requirements::statePending()
{
// 2.1.1. When pending, a promise:
// 2.1.1.1. may transition to either the fulfilled state
{
QPromise<> p;
QPromise<int> p([&](const QPromiseResolve<int>& resolve) {
qtpromise_defer([=]() { resolve(42); });
});
QVERIFY(p.isPending());
QVERIFY(!p.isFulfilled());
QVERIFY(!p.isRejected());
}
// 2.1.1.1. may transition to either the fulfilled state
{
QPromise<> p;
p.fulfill();
p.wait();
QVERIFY(!p.isPending());
QVERIFY(p.isFulfilled());
QVERIFY(!p.isRejected());
}
// 2.1.1.1. ... or the rejected state
{
QPromise<> p;
p.reject("foo");
{
QPromise<int> p([&](const QPromiseResolve<int>&, const QPromiseReject<int>& reject) {
qtpromise_defer([=]() { reject(QString("foo")); });
});
QVERIFY(p.isPending());
QVERIFY(!p.isFulfilled());
QVERIFY(!p.isRejected());
p.wait();
QVERIFY(!p.isPending());
QVERIFY(!p.isFulfilled());
QVERIFY(p.isRejected());
}
}
void tst_requirements::stateFulfilled()
{
QVector<int> values;
auto log_value = [&values](int res) { values << res; };
QPromise<int> p;
QVERIFY(p.isPending());
QString error;
int value = -1;
// 2.1.2. When fulfilled, a promise:
p.fulfill(42).then(log_value).wait();
QPromise<int> p([](
const QPromiseResolve<int>& resolve,
const QPromiseReject<int>& reject) {
qtpromise_defer([=]() {
// 2.1.2.2. must have a value, which must not change.
resolve(42);
resolve(43);
// 2.1.2.1. must not transition to any other state.
reject(QString("foo"));
});
});
QVERIFY(p.isPending());
p.then([&](int res) {
value = res;
}, [&](const QString& err) {
error = err;
}).wait();
QVERIFY(p.isFulfilled());
QVERIFY(!p.isRejected());
// 2.1.2.1. must not transition to any other state.
p.reject("foo").then(log_value).wait();
QVERIFY(p.isFulfilled());
QVERIFY(!p.isRejected());
// 2.1.2.2. must have a value, which must not change.
p.fulfill(51).then(log_value).wait();
QVERIFY(p.isFulfilled());
QVERIFY(!p.isRejected());
QCOMPARE(values, QVector<int>({42, 42, 42}));
QVERIFY(error.isEmpty());
QCOMPARE(value, 42);
}
void tst_requirements::stateRejected()
{
QStringList errors;
auto log_error = [&errors](const QString& err) { errors << err; return -1; };
QPromise<int> p;
QVERIFY(p.isPending());
QString error;
int value = -1;
// 2.1.3 When rejected, a promise:
p.reject(QString("foo")).then(nullptr, log_error).wait();
QPromise<int> p([](
const QPromiseResolve<int>& resolve,
const QPromiseReject<int>& reject) {
qtpromise_defer([=]() {
// 2.1.3.2. must have a reason, which must not change.
reject(QString("foo"));
reject(QString("bar"));
// 2.1.3.1. must not transition to any other state.
resolve(42);
});
});
QVERIFY(p.isPending());
p.then([&](int res) {
value = res;
}, [&](const QString& err) {
error = err;
}).wait();
QVERIFY(!p.isFulfilled());
QVERIFY(p.isRejected());
// 2.1.3.1. must not transition to any other state.
p.fulfill(51).then(nullptr, log_error).wait();
QVERIFY(!p.isFulfilled());
QVERIFY(p.isRejected());
// 2.1.3.2. must have a reason, which must not change.
p.reject(QString("bar")).then(nullptr, log_error).wait();
QVERIFY(!p.isFulfilled());
QVERIFY(p.isRejected());
QCOMPARE(errors, QStringList({"foo", "foo", "foo"}));
QCOMPARE(error, QString("foo"));
QCOMPARE(value, -1);
}
void tst_requirements::thenArguments()
{
// Both onFulfilled and onRejected are given
// 2.2.1. Both onFulfilled and onRejected are given
{
int value = 0;
QPromise<int> p;
p.fulfill(42).then([&value](int res) { value = res; }, [](const QString&){}).wait();
QVERIFY(p.isFulfilled());
QString error;
int value = -1;
QPromise<int>::resolve(42).then(
[&](int res) { value = res; },
[&](const QString& err) { error = err; }
).wait();
QVERIFY(error.isEmpty());
QCOMPARE(value, 42);
}
{
QString error;
QPromise<int> p;
p.reject(QString("foo")).then([](int) {}, [&error](const QString& err){ error = err; }).wait();
QVERIFY(p.isRejected());
int value = -1;
QPromise<int>::reject(QString("foo")).then(
[&](int res) { value = res; },
[&](const QString& err){ error = err; }
).wait();
QCOMPARE(error, QString("foo"));
QCOMPARE(value, -1);
}
// 2.2.1. onFulfilled is an optional arguments:
{
QString error;
QPromise<int> p;
p.reject(QString("foo")).then(nullptr, [&error](const QString& err){ error = err; return 42; }).wait();
QVERIFY(p.isRejected());
QPromise<int>::reject(QString("foo")).then(
nullptr,
[&](const QString& err){ error = err; return 42; }
).wait();
QCOMPARE(error, QString("foo"));
}
// 2.2.1. onRejected is an optional arguments:
{
int value = 0;
QPromise<int> p;
p.fulfill(42).then([&value](int res) { value = res; }).wait();
QVERIFY(p.isFulfilled());
int value = -1;
QPromise<int>::resolve(42).then(
[&value](int res) { value = res; }
).wait();
QCOMPARE(value, 42);
}
@ -157,55 +194,57 @@ void tst_requirements::thenArguments()
void tst_requirements::thenOnFulfilled()
{
// 2.2.2. If onFulfilled is a function:
QPromise<int> p0;
QVector<int> values;
auto p1 = p0.then([&values](int res) { values << res; }); // @TODO .wait(10)
QPromise<int> p0([](const QPromiseResolve<int>& resolve) {
qtpromise_defer([=]() {
// 2.2.2.3. it must not be called more than once
resolve(42);
resolve(43);
});
});
auto p1 = p0.then([&](int res) { values << res; });
// 2.2.2.2. it must not be called before promise is fulfilled.
QVERIFY(p0.isPending());
QVERIFY(p1.isPending());
QVERIFY(values.isEmpty());
p1.wait();
// 2.2.2.1. it must be called after promise is fulfilled,
// with promises value as its first argument.
p0.fulfill(42);
p1.wait();
QVERIFY(p0.isFulfilled());
QVERIFY(p1.isFulfilled());
QCOMPARE(values, QVector<int>({42}));
// 2.2.2.3. it must not be called more than once
p0.fulfill(12);
p1.wait();
QCOMPARE(values, QVector<int>({42}));
}
void tst_requirements::thenOnRejected()
{
// 2.2.3. If onRejected is a function:
QPromise<> p0;
QStringList errors;
auto p1 = p0.then(nullptr, [&errors](const QString& err) { errors << err; }); // @TODO .wait(10)
QPromise<void> p0([](const QPromiseResolve<void>&, const QPromiseReject<void>& reject) {
qtpromise_defer([=]() {
// 2.2.3.3. it must not be called more than once.
reject(QString("foo"));
reject(QString("bar"));
});
});
auto p1 = p0.then(nullptr, [&](const QString& err) { errors << err; });
// 2.2.3.2. it must not be called before promise is rejected.
QVERIFY(p0.isPending());
QVERIFY(p1.isPending());
QVERIFY(errors.isEmpty());
p1.wait();
// 2.2.3.1. it must be called after promise is rejected,
// with promises reason as its first argument.
p0.reject(QString("foo"));
p1.wait();
QVERIFY(p0.isRejected());
QVERIFY(p1.isFulfilled());
QCOMPARE(errors, QStringList({"foo"}));
// 2.2.2.3. it must not be called more than once.
p0.reject(12);
p1.wait();
QCOMPARE(errors, QStringList({"foo"}));
}
void tst_requirements::thenAsynchronous()
@ -214,14 +253,13 @@ void tst_requirements::thenAsynchronous()
// stack contains only platform code (ie. executed asynchronously, after the event
// loop turn in which then is called, and with a fresh stack).
int value = 0;
QPromise<int> p0;
p0.fulfill(42);
int value = -1;
auto p0 = QPromise<int>::resolve(42);
QVERIFY(p0.isFulfilled());
auto p1 = p0.then([&value](int res){ value = res; });
auto p1 = p0.then([&](int res){ value = res; });
QVERIFY(p1.isPending());
QCOMPARE(value, 0);
QCOMPARE(value, -1);
p1.wait();
QVERIFY(p1.isFulfilled());
@ -235,32 +273,38 @@ void tst_requirements::thenMultipleCalls()
// 2.2.6.1. If/when promise is fulfilled, all respective onFulfilled callbacks
// must execute in the order of their originating calls to then:
{
QPromise<int> p;
QVector<int> values;
auto all = qPromiseAll(QVector<QPromise<void> >{
p.then([&values](int r) { values << r + 1; }),
p.then([&values](int r) { values << r + 2; }),
p.then([&values](int r) { values << r + 3; })
QPromise<int> p([](const QPromiseResolve<int>& resolve) {
qtpromise_defer([=]() {
resolve(42);
});
});
p.fulfill(42);
all.wait();
qPromiseAll(QVector<QPromise<void> >{
p.then([&](int r) { values << r + 1; }),
p.then([&](int r) { values << r + 2; }),
p.then([&](int r) { values << r + 3; })
}).wait();
QCOMPARE(values, QVector<int>({43, 44, 45}));
}
// 2.2.6.2. If/when promise is rejected, all respective onRejected callbacks
// must execute in the order of their originating calls to then:
{
QPromise<int> p;
QVector<int> values;
auto all = qPromiseAll(QVector<QPromise<int> >{
p.then(nullptr, [&values](int r) { values << r + 1; return r + 1; }),
p.then(nullptr, [&values](int r) { values << r + 2; return r + 2; }),
p.then(nullptr, [&values](int r) { values << r + 3; return r + 3; })
QPromise<int> p([](const QPromiseResolve<int>&, const QPromiseReject<int>& reject) {
qtpromise_defer([=]() {
reject(8);
});
});
p.reject(8);
all.wait();
qPromiseAll(QVector<QPromise<int> >{
p.then(nullptr, [&](int r) { values << r + 1; return r + 1; }),
p.then(nullptr, [&](int r) { values << r + 2; return r + 2; }),
p.then(nullptr, [&](int r) { values << r + 3; return r + 3; })
}).wait();
QCOMPARE(values, QVector<int>({9, 10, 11}));
}
}
@ -269,8 +313,8 @@ void tst_requirements::thenHandlers()
{
// 2.2.7. then must return a promise: p2 = p1.then(onFulfilled, onRejected);
{
QPromise<int> p1;
auto handler = [](){ return 42; };
auto p1 = QPromise<int>::resolve(42);
Q_STATIC_ASSERT((std::is_same<decltype(p1.then(handler, nullptr)), QPromise<int> >::value));
Q_STATIC_ASSERT((std::is_same<decltype(p1.then(nullptr, handler)), QPromise<int> >::value));
Q_STATIC_ASSERT((std::is_same<decltype(p1.then(handler, handler)), QPromise<int> >::value));
@ -283,19 +327,21 @@ void tst_requirements::thenHandlers()
// p2 must be rejected with e as the reason.
{
QString reason;
QPromise<int> p1;
auto p2 = p1.fulfill(42).then([](){ throw QString("foo"); });
p2.then(nullptr, [&reason](const QString& e) { reason = e; }).wait();
auto p1 = QPromise<int>::resolve(42);
auto p2 = p1.then([](){ throw QString("foo"); });
p2.then(nullptr, [&](const QString& e) { reason = e; }).wait();
QVERIFY(p1.isFulfilled());
QVERIFY(p2.isRejected());
QCOMPARE(reason, QString("foo"));
}
{
QString reason;
QPromise<int> p1;
auto p2 = p1.reject(QString("foo")).then(nullptr, [](){ throw QString("bar"); return 42; });
p2.then(nullptr, [&reason](const QString& e) { reason = e; return 0; }).wait();
auto p1 = QPromise<int>::reject(QString("foo"));
auto p2 = p1.then(nullptr, [](){ throw QString("bar"); return 42; });
p2.then(nullptr, [&](const QString& e) { reason = e; return 0; }).wait();
QVERIFY(p1.isRejected());
QVERIFY(p2.isRejected());
QCOMPARE(reason, QString("bar"));
}
@ -304,11 +350,12 @@ void tst_requirements::thenHandlers()
// p2 must be fulfilled with the same value as promise1
{
QString value;
QPromise<QString> p1;
auto p2 = p1.fulfill("42").then(nullptr, [](){ return QString(); });
auto p1 = QPromise<QString>::resolve("42");
auto p2 = p1.then(nullptr, [](){ return QString(); });
Q_STATIC_ASSERT((std::is_same<decltype(p2), QPromise<QString> >::value));
p2.then([&value](const QString& e) { value = e; }).wait();
p2.then([&](const QString& e) { value = e; }).wait();
QVERIFY(p1.isFulfilled());
QVERIFY(p2.isFulfilled());
QCOMPARE(value, QString("42"));
}