mirror of
https://github.com/simonbrunel/qtpromise.git
synced 2024-11-21 18:24:29 +08:00
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:
parent
6a642446df
commit
ce3ed72dd4
@ -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"
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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()> >;
|
||||
|
||||
|
@ -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
|
||||
|
42
src/qtpromise/qpromiseerror.h
Normal file
42
src/qtpromise/qpromiseerror.h
Normal 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
|
@ -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
|
||||
|
@ -3,4 +3,5 @@ HEADERS += \
|
||||
$$PWD/qpromise.inl \
|
||||
$$PWD/qpromise_p.h \
|
||||
$$PWD/qpromise_qfuture.inl \
|
||||
$$PWD/qpromiseerror.h \
|
||||
$$PWD/qpromiseglobal.h
|
||||
|
@ -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"));
|
||||
}
|
||||
|
@ -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 promise’s 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 promise’s 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"));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user