mirror of
https://github.com/simonbrunel/qtpromise.git
synced 2024-11-22 02:34:30 +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
|
#ifndef _QTPROMISE_QPROMISE_H
|
||||||
#define _QTPROMISE_QPROMISE_H
|
#define _QTPROMISE_QPROMISE_H
|
||||||
|
|
||||||
// QPromise
|
// QtPromise
|
||||||
#include "qpromise_p.h"
|
#include "qpromise_p.h"
|
||||||
#include "qpromiseglobal.h"
|
#include "qpromiseglobal.h"
|
||||||
|
|
||||||
// Qt
|
// Qt
|
||||||
#include <QExplicitlySharedDataPointer>
|
#include <QExplicitlySharedDataPointer>
|
||||||
#include <QVariant>
|
|
||||||
|
|
||||||
namespace QtPromise {
|
namespace QtPromise {
|
||||||
|
|
||||||
using namespace QtPromisePrivate;
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class QPromiseBase
|
class QPromiseBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using Type = T;
|
using Type = T;
|
||||||
|
|
||||||
QPromiseBase() : m_d(new QPromiseData<T>()) { }
|
|
||||||
virtual ~QPromiseBase() { }
|
virtual ~QPromiseBase() { }
|
||||||
|
|
||||||
bool isFulfilled() const { return m_d->resolved && !m_d->rejected; }
|
bool isFulfilled() const { return m_d->resolved && !m_d->rejected; }
|
||||||
@ -27,22 +23,34 @@ public:
|
|||||||
bool isPending() const { return !m_d->resolved; }
|
bool isPending() const { return !m_d->resolved; }
|
||||||
|
|
||||||
template <typename TFulfilled, typename TRejected = std::nullptr_t>
|
template <typename TFulfilled, typename TRejected = std::nullptr_t>
|
||||||
inline auto then(TFulfilled fulfilled, TRejected rejected = nullptr)
|
inline typename QtPromisePrivate::PromiseHandler<T, TFulfilled>::Promise
|
||||||
-> typename QPromiseHandler<T, TFulfilled>::Promise;
|
then(TFulfilled fulfilled, TRejected rejected = nullptr);
|
||||||
|
|
||||||
template <typename TRejected>
|
template <typename TRejected>
|
||||||
inline auto fail(TRejected rejected)
|
inline typename QtPromisePrivate::PromiseHandler<T, std::nullptr_t>::Promise
|
||||||
-> typename QPromiseHandler<T, std::nullptr_t>::Promise;
|
fail(TRejected rejected);
|
||||||
|
|
||||||
template <typename TError>
|
|
||||||
inline QPromise<T> reject(const TError& error);
|
|
||||||
|
|
||||||
inline QPromise<T> wait() const;
|
inline QPromise<T> wait() const;
|
||||||
|
|
||||||
protected:
|
public: // STATIC
|
||||||
QExplicitlySharedDataPointer<QPromiseData<T> > m_d;
|
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();
|
inline void dispatch();
|
||||||
};
|
};
|
||||||
@ -51,42 +59,41 @@ template <typename T>
|
|||||||
class QPromise: public QPromiseBase<T>
|
class QPromise: public QPromiseBase<T>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
QPromise() : QPromiseBase<T>() {}
|
template <typename F>
|
||||||
|
QPromise(F resolver): QPromiseBase<T>(resolver) { }
|
||||||
|
|
||||||
template <typename THandler>
|
template <typename THandler>
|
||||||
inline QPromise<T> finally(THandler handler);
|
inline QPromise<T> finally(THandler handler);
|
||||||
|
|
||||||
inline QPromise<T> fulfill(const T& value);
|
|
||||||
|
|
||||||
public: // STATIC
|
public: // STATIC
|
||||||
inline static QPromise<QVector<T> > all(const QVector<QPromise<T> >& promises);
|
inline static QPromise<QVector<T> > all(const QVector<QPromise<T> >& promises);
|
||||||
|
inline static QPromise<T> resolve(const T& value);
|
||||||
|
|
||||||
protected:
|
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:
|
private:
|
||||||
friend class QPromiseBase<T>;
|
friend class QPromiseBase<T>;
|
||||||
|
|
||||||
QPromise(const QPromiseBase<T>& p) : QPromiseBase<T>(p) { }
|
QPromise(const QPromiseBase<T>& p) : QPromiseBase<T>(p) { }
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
class QPromise<void>: public QPromiseBase<void>
|
class QPromise<void>: public QPromiseBase<void>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
QPromise(): QPromiseBase<void>() {}
|
template <typename F>
|
||||||
|
QPromise(F resolver): QPromiseBase<void>(resolver) { }
|
||||||
|
|
||||||
template <typename THandler>
|
template <typename THandler>
|
||||||
inline QPromise<void> finally(THandler handler);
|
inline QPromise<void> finally(THandler handler);
|
||||||
|
|
||||||
inline QPromise<void> fulfill();
|
|
||||||
|
|
||||||
public: // STATIC
|
public: // STATIC
|
||||||
inline static QPromise<void> all(const QVector<QPromise<void> >& promises);
|
inline static QPromise<void> all(const QVector<QPromise<void> >& promises);
|
||||||
|
inline static QPromise<void> resolve();
|
||||||
|
|
||||||
protected:
|
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:
|
private:
|
||||||
friend class QPromiseBase<void>;
|
friend class QPromiseBase<void>;
|
||||||
@ -94,13 +101,8 @@ private:
|
|||||||
QPromise(const QPromiseBase<void>& p) : QPromiseBase<void>(p) { }
|
QPromise(const QPromiseBase<void>& p) : QPromiseBase<void>(p) { }
|
||||||
};
|
};
|
||||||
|
|
||||||
using QVariantPromise = QtPromise::QPromise<QVariant>;
|
|
||||||
|
|
||||||
} // namespace QtPromise
|
} // namespace QtPromise
|
||||||
|
|
||||||
Q_DECLARE_TYPEINFO(QtPromise::QVariantPromise, Q_MOVABLE_TYPE);
|
|
||||||
Q_DECLARE_METATYPE(QtPromise::QVariantPromise)
|
|
||||||
|
|
||||||
#include "qpromise.inl"
|
#include "qpromise.inl"
|
||||||
#include "qpromise_qfuture.inl"
|
#include "qpromise_qfuture.inl"
|
||||||
|
|
||||||
|
@ -4,15 +4,91 @@
|
|||||||
|
|
||||||
namespace QtPromise {
|
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 T>
|
||||||
template <typename TFulfilled, typename TRejected>
|
template <typename TFulfilled, typename TRejected>
|
||||||
inline auto QPromiseBase<T>::then(TFulfilled fulfilled, TRejected rejected)
|
inline typename QtPromisePrivate::PromiseHandler<T, TFulfilled>::Promise
|
||||||
-> typename QPromiseHandler<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);
|
PromiseType next([=](
|
||||||
m_d->catchers << QPromiseCatcher<T, TRejected>::create(next, rejected);
|
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) {
|
if (m_d->resolved) {
|
||||||
dispatch();
|
dispatch();
|
||||||
@ -23,26 +99,12 @@ inline auto QPromiseBase<T>::then(TFulfilled fulfilled, TRejected rejected)
|
|||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
template <typename TRejected>
|
template <typename TRejected>
|
||||||
inline auto QPromiseBase<T>::fail(TRejected rejected)
|
inline typename QtPromisePrivate::PromiseHandler<T, std::nullptr_t>::Promise
|
||||||
-> typename QPromiseHandler<T, std::nullptr_t>::Promise
|
QPromiseBase<T>::fail(TRejected rejected)
|
||||||
{
|
{
|
||||||
return then(nullptr, 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>
|
template <typename T>
|
||||||
inline QPromise<T> QPromiseBase<T>::wait() const
|
inline QPromise<T> QPromiseBase<T>::wait() const
|
||||||
{
|
{
|
||||||
@ -55,20 +117,67 @@ inline QPromise<T> QPromiseBase<T>::wait() const
|
|||||||
return *this;
|
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>
|
template <typename T>
|
||||||
inline void QPromiseBase<T>::dispatch()
|
inline void QPromiseBase<T>::dispatch()
|
||||||
{
|
{
|
||||||
|
using namespace QtPromisePrivate;
|
||||||
|
|
||||||
Q_ASSERT(m_d->resolved);
|
Q_ASSERT(m_d->resolved);
|
||||||
|
|
||||||
typename QPromiseData<T>::HandlerList handlers;
|
typename PromiseData<T>::HandlerList handlers;
|
||||||
typename QPromiseData<T>::CatcherList catchers;
|
typename PromiseData<T>::CatcherList catchers;
|
||||||
|
|
||||||
handlers.swap(m_d->handlers);
|
handlers.swap(m_d->handlers);
|
||||||
catchers.swap(m_d->catchers);
|
catchers.swap(m_d->catchers);
|
||||||
|
|
||||||
if (m_d->rejected) {
|
if (m_d->rejected) {
|
||||||
const std::exception_ptr error = m_d->error;
|
const QPromiseError error = m_d->error;
|
||||||
qtpromise_defer([catchers, error]() {
|
qtpromise_defer([=]() {
|
||||||
for (auto catcher: catchers) {
|
for (auto catcher: catchers) {
|
||||||
catcher(error);
|
catcher(error);
|
||||||
}
|
}
|
||||||
@ -83,67 +192,62 @@ template <typename THandler>
|
|||||||
inline QPromise<T> QPromise<T>::finally(THandler handler)
|
inline QPromise<T> QPromise<T>::finally(THandler handler)
|
||||||
{
|
{
|
||||||
return this->then([=](const T& res) {
|
return this->then([=](const T& res) {
|
||||||
return QPromise<void>().fulfill().then(handler).then([=](){
|
return QPromise<void>::resolve().then(handler).then([=](){
|
||||||
return res;
|
return res;
|
||||||
});
|
});
|
||||||
}, [=]() {
|
}, [=]() {
|
||||||
const auto exception = std::current_exception();
|
const auto exception = std::current_exception();
|
||||||
return QPromise<void>().fulfill().then(handler).then([=](){
|
return QPromise<void>::resolve().then(handler).then([=](){
|
||||||
std::rethrow_exception(exception);
|
std::rethrow_exception(exception);
|
||||||
return T();
|
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>
|
template <typename T>
|
||||||
inline QPromise<QVector<T> > QPromise<T>::all(const QVector<QPromise<T> >& promises)
|
inline QPromise<QVector<T> > QPromise<T>::all(const QVector<QPromise<T> >& promises)
|
||||||
{
|
{
|
||||||
QPromise<QVector<T> > next;
|
|
||||||
|
|
||||||
const int count = promises.size();
|
const int count = promises.size();
|
||||||
if (count == 0) {
|
if (count == 0) {
|
||||||
return next.fulfill({});
|
return QPromise<QVector<T> >::resolve({});
|
||||||
}
|
}
|
||||||
|
|
||||||
QSharedPointer<int> remaining(new int(count));
|
return QPromise<QVector<T> >([=](
|
||||||
QSharedPointer<QVector<T> > results(new QVector<T>(count));
|
const QPromiseResolve<QVector<T> >& resolve,
|
||||||
|
const QPromiseReject<QVector<T> >& reject) {
|
||||||
|
|
||||||
for (int i=0; i<count; ++i) {
|
QSharedPointer<int> remaining(new int(count));
|
||||||
QPromise<T>(promises[i]).then([=](const T& res) mutable {
|
QSharedPointer<QVector<T> > results(new QVector<T>(count));
|
||||||
if (next.isPending()) {
|
|
||||||
|
for (int i=0; i<count; ++i) {
|
||||||
|
QPromise<T>(promises[i]).then([=](const T& res) mutable {
|
||||||
(*results)[i] = res;
|
(*results)[i] = res;
|
||||||
if (--(*remaining) == 0) {
|
if (--(*remaining) == 0) {
|
||||||
next.fulfill(*results);
|
resolve(*results);
|
||||||
}
|
}
|
||||||
}
|
}, [=]() mutable {
|
||||||
}, [=]() mutable {
|
if (*remaining != -1) {
|
||||||
if (next.isPending()) {
|
*remaining = -1;
|
||||||
next.reject(std::current_exception());
|
reject(std::current_exception());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
});
|
||||||
return next;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
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;
|
const T value = this->m_d->value;
|
||||||
qtpromise_defer([handlers, value]() {
|
QtPromisePrivate::qtpromise_defer([handlers, value]() {
|
||||||
for (auto handler: handlers) {
|
for (auto handler: handlers) {
|
||||||
handler(value);
|
handler(value);
|
||||||
}
|
}
|
||||||
@ -154,52 +258,53 @@ template <typename THandler>
|
|||||||
inline QPromise<void> QPromise<void>::finally(THandler handler)
|
inline QPromise<void> QPromise<void>::finally(THandler handler)
|
||||||
{
|
{
|
||||||
return this->then([=]() {
|
return this->then([=]() {
|
||||||
return QPromise<void>().fulfill().then(handler).then([](){});
|
return QPromise<void>::resolve().then(handler).then([](){});
|
||||||
}, [=]() {
|
}, [=]() {
|
||||||
const auto exception = std::current_exception();
|
const auto exception = std::current_exception();
|
||||||
return QPromise<void>().fulfill().then(handler).then([=](){
|
return QPromise<void>::resolve().then(handler).then([=](){
|
||||||
std::rethrow_exception(exception);
|
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)
|
inline QPromise<void> QPromise<void>::all(const QVector<QPromise<void> >& promises)
|
||||||
{
|
{
|
||||||
QPromise<void> next;
|
const int count = promises.size();
|
||||||
|
if (count == 0) {
|
||||||
QSharedPointer<int> remaining(new int(promises.size()));
|
return QPromise<void>::resolve();
|
||||||
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
for (const auto& handler: handlers) {
|
||||||
handler();
|
handler();
|
||||||
}
|
}
|
||||||
@ -209,9 +314,23 @@ inline void QPromise<void>::notify(const typename QPromiseData<void>::HandlerLis
|
|||||||
// Helpers
|
// Helpers
|
||||||
|
|
||||||
template <typename T>
|
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>
|
template <typename T>
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
#ifndef _QTPROMISE_QPROMISE_P_H
|
#ifndef _QTPROMISE_QPROMISE_P_H
|
||||||
#define _QTPROMISE_QPROMISE_P_H
|
#define _QTPROMISE_QPROMISE_P_H
|
||||||
|
|
||||||
// QPromise
|
// QtPromise
|
||||||
|
#include "qpromiseerror.h"
|
||||||
#include "qpromiseglobal.h"
|
#include "qpromiseglobal.h"
|
||||||
|
|
||||||
// Qt
|
// Qt
|
||||||
@ -10,18 +11,21 @@
|
|||||||
#include <QSharedData>
|
#include <QSharedData>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
|
|
||||||
// STL
|
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
namespace QtPromise {
|
namespace QtPromise {
|
||||||
template <typename T = void>
|
|
||||||
|
template <typename T>
|
||||||
class QPromise;
|
class QPromise;
|
||||||
}
|
|
||||||
|
template <typename T>
|
||||||
|
class QPromiseResolve;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class QPromiseReject;
|
||||||
|
|
||||||
|
} // namespace QtPromise
|
||||||
|
|
||||||
namespace QtPromisePrivate {
|
namespace QtPromisePrivate {
|
||||||
|
|
||||||
using namespace QtPromise;
|
|
||||||
|
|
||||||
template <typename F>
|
template <typename F>
|
||||||
inline void qtpromise_defer(F f)
|
inline void qtpromise_defer(F f)
|
||||||
{
|
{
|
||||||
@ -29,265 +33,269 @@ inline void qtpromise_defer(F f)
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct QPromiseDeduce
|
struct PromiseDeduce
|
||||||
{
|
{
|
||||||
using Type = QPromise<Unqualified<T> >;
|
using Type = QtPromise::QPromise<Unqualified<T> >;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct QPromiseDeduce<QPromise<T> >
|
struct PromiseDeduce<QtPromise::QPromise<T> >
|
||||||
: public QPromiseDeduce<T>
|
: public PromiseDeduce<T>
|
||||||
{ };
|
{ };
|
||||||
|
|
||||||
template <typename T, typename TPromise = typename QPromiseDeduce<T>::Type>
|
template <typename T>
|
||||||
struct QPromiseFulfill
|
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>
|
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(
|
promise.then(
|
||||||
[=](const T& value) mutable {
|
[=](const T& value) {
|
||||||
next.fulfill(value);
|
resolve(value);
|
||||||
},
|
},
|
||||||
[=]() mutable {
|
[=]() { // catch all
|
||||||
next.reject(std::current_exception());
|
reject(std::current_exception());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct QPromiseFulfill<QPromise<void>, QPromise<void> >
|
struct PromiseFulfill<QtPromise::QPromise<void> >
|
||||||
{
|
{
|
||||||
template <typename TPromise>
|
template <typename TPromise, typename TResolve, typename TReject>
|
||||||
static void call(TPromise next, TPromise promise)
|
static void call(TPromise promise, TResolve resolve, TReject reject)
|
||||||
{
|
{
|
||||||
promise.then(
|
promise.then(
|
||||||
[=]() mutable {
|
[=]() {
|
||||||
next.fulfill();
|
resolve();
|
||||||
},
|
},
|
||||||
[=]() mutable {
|
[=]() { // catch all
|
||||||
next.reject(std::current_exception());
|
reject(std::current_exception());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T, typename TRes>
|
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>;
|
using ResType = Unqualified<TRes>;
|
||||||
|
|
||||||
template <typename TPromise, typename THandler>
|
template <typename THandler, typename TResolve, typename TReject>
|
||||||
static void call(TPromise next, THandler handler, const T& value)
|
static void call(const T& value, THandler handler, TResolve resolve, TReject reject)
|
||||||
{
|
{
|
||||||
ResType res;
|
|
||||||
try {
|
try {
|
||||||
res = handler(value);
|
const ResType res = handler(value);
|
||||||
|
PromiseFulfill<ResType>::call(res, resolve, reject);
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
next.reject(std::current_exception());
|
reject(std::current_exception());
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
QPromiseFulfill<ResType>::call(next, res);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
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>
|
template <typename THandler, typename TResolve, typename TReject>
|
||||||
static void call(TPromise next, THandler handler, const T& value)
|
static void call(const T& value, THandler handler, TResolve resolve, TReject reject)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
handler(value);
|
handler(value);
|
||||||
|
resolve();
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
next.reject(std::current_exception());
|
reject(std::current_exception());
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
next.fulfill();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename TRes>
|
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>;
|
using ResType = Unqualified<TRes>;
|
||||||
|
|
||||||
template <typename TPromise, typename THandler>
|
template <typename THandler, typename TResolve, typename TReject>
|
||||||
static void call(TPromise next, THandler handler)
|
static void call(THandler handler, TResolve resolve, TReject reject)
|
||||||
{
|
{
|
||||||
ResType res;
|
|
||||||
try {
|
try {
|
||||||
res = handler();
|
const ResType res = handler();
|
||||||
|
PromiseFulfill<ResType>::call(res, resolve, reject);
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
next.reject(std::current_exception());
|
reject(std::current_exception());
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
QPromiseFulfill<ResType>::call(next, res);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct QPromiseDispatch<void, void>
|
struct PromiseDispatch<void, void>
|
||||||
{
|
{
|
||||||
using Promise = QPromise<void>;
|
using Promise = QtPromise::QPromise<void>;
|
||||||
|
|
||||||
template <typename TPromise, typename THandler>
|
template <typename THandler, typename TResolve, typename TReject>
|
||||||
static void call(TPromise next, THandler handler)
|
static void call(THandler handler, TResolve resolve, TReject reject)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
handler();
|
handler();
|
||||||
|
resolve();
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
next.reject(std::current_exception());
|
reject(std::current_exception());
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
next.fulfill();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T, typename THandler, typename TArg = typename ArgsOf<THandler>::first>
|
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 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 {
|
return [=](const T& value) {
|
||||||
QPromiseDispatch<T, ResType>::call(next, handler, value);
|
PromiseDispatch<T, ResType>::call(value, handler, resolve, reject);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T, typename THandler>
|
template <typename T, typename THandler>
|
||||||
struct QPromiseHandler<T, THandler, void>
|
struct PromiseHandler<T, THandler, void>
|
||||||
{
|
{
|
||||||
using ResType = typename std::result_of<THandler()>::type;
|
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 {
|
return [=](const T&) {
|
||||||
QPromiseDispatch<void, ResType>::call(next, handler);
|
PromiseDispatch<void, ResType>::call(handler, resolve, reject);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename THandler>
|
template <typename THandler>
|
||||||
struct QPromiseHandler<void, THandler, void>
|
struct PromiseHandler<void, THandler, void>
|
||||||
{
|
{
|
||||||
using ResType = typename std::result_of<THandler()>::type;
|
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 [=]() {
|
return [=]() {
|
||||||
QPromiseDispatch<void, ResType>::call(next, handler);
|
PromiseDispatch<void, ResType>::call(handler, resolve, reject);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
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,
|
// 2.2.7.3. If onFulfilled is not a function and promise1 is fulfilled,
|
||||||
// promise2 must be fulfilled with the same value as promise1.
|
// promise2 must be fulfilled with the same value as promise1.
|
||||||
QPromiseFulfill<T>::call(next, value);
|
PromiseFulfill<T>::call(value, resolve, reject);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
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>
|
template <typename TResolve, typename TReject>
|
||||||
static std::function<void()> create(const TPromise& next, std::nullptr_t)
|
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,
|
// 2.2.7.3. If onFulfilled is not a function and promise1 is fulfilled,
|
||||||
// promise2 must be fulfilled with the same value as promise1.
|
// 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>
|
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;
|
using ResType = typename std::result_of<THandler(TArg)>::type;
|
||||||
|
|
||||||
template <typename TPromise>
|
template <typename TResolve, typename TReject>
|
||||||
static Type create(const TPromise& next, THandler handler)
|
static Functor create(THandler handler, TResolve resolve, TReject reject)
|
||||||
{
|
{
|
||||||
return [=](const std::exception_ptr& eptr) mutable {
|
return [=](const QtPromise::QPromiseError& error) {
|
||||||
try {
|
try {
|
||||||
std::rethrow_exception(eptr);
|
error.rethrow();
|
||||||
} catch (const TArg& error) {
|
} catch (const TArg& error) {
|
||||||
QPromiseDispatch<TArg, ResType>::call(next, handler, error);
|
PromiseDispatch<TArg, ResType>::call(error, handler, resolve, reject);
|
||||||
} catch(...) {
|
} catch(...) {
|
||||||
TPromise(next).reject(std::current_exception());
|
reject(std::current_exception());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T, typename THandler>
|
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;
|
using ResType = typename std::result_of<THandler()>::type;
|
||||||
|
|
||||||
template <typename TPromise>
|
template <typename TResolve, typename TReject>
|
||||||
static Type create(const TPromise& next, THandler handler)
|
static Functor create(THandler handler, TResolve resolve, TReject reject)
|
||||||
{
|
{
|
||||||
return [=](const std::exception_ptr& eptr) mutable {
|
return [=](const QtPromise::QPromiseError& error) {
|
||||||
try {
|
try {
|
||||||
std::rethrow_exception(eptr);
|
error.rethrow();
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
QPromiseDispatch<void, ResType>::call(next, handler);
|
PromiseDispatch<void, ResType>::call(handler, resolve, reject);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
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>
|
template <typename TResolve, typename TReject>
|
||||||
static Type create(const TPromise& next, std::nullptr_t)
|
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,
|
// 2.2.7.4. If onRejected is not a function and promise1 is rejected,
|
||||||
// promise2 must be rejected with the same reason as promise1
|
// promise2 must be rejected with the same reason as promise1
|
||||||
TPromise(next).reject(eptr);
|
reject(error);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class T>
|
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)> >;
|
using CatcherList = QVector<std::function<void(ErrorType)> >;
|
||||||
|
|
||||||
bool resolved;
|
bool resolved;
|
||||||
@ -297,7 +305,7 @@ struct QPromiseDataBase: public QSharedData
|
|||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct QPromiseData: QPromiseDataBase<T>
|
struct PromiseData: PromiseDataBase<T>
|
||||||
{
|
{
|
||||||
using HandlerList = QVector<std::function<void(T)> >;
|
using HandlerList = QVector<std::function<void(T)> >;
|
||||||
|
|
||||||
@ -306,7 +314,7 @@ struct QPromiseData: QPromiseDataBase<T>
|
|||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct QPromiseData<void>: QPromiseDataBase<void>
|
struct PromiseData<void>: PromiseDataBase<void>
|
||||||
{
|
{
|
||||||
using HandlerList = QVector<std::function<void()> >;
|
using HandlerList = QVector<std::function<void()> >;
|
||||||
|
|
||||||
|
@ -4,76 +4,61 @@
|
|||||||
namespace QtPromisePrivate {
|
namespace QtPromisePrivate {
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct QPromiseDeduce<QFuture<T> >
|
struct PromiseDeduce<QFuture<T> >
|
||||||
: public QPromiseDeduce<T>
|
: public PromiseDeduce<T>
|
||||||
{ };
|
{ };
|
||||||
|
|
||||||
template <typename 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>;
|
using Watcher = QFutureWatcher<T>;
|
||||||
|
|
||||||
Watcher* watcher = new Watcher();
|
Watcher* watcher = new Watcher();
|
||||||
QObject::connect(
|
QObject::connect(watcher, &Watcher::finished, [=]() mutable {
|
||||||
watcher, &Watcher::finished,
|
try {
|
||||||
[next, watcher]() mutable {
|
T res = watcher->result();
|
||||||
T res;
|
PromiseFulfill<T>::call(res, resolve, reject);
|
||||||
try {
|
} catch(...) {
|
||||||
res = watcher->result();
|
reject(std::current_exception());
|
||||||
} catch(...) {
|
}
|
||||||
next.reject(std::current_exception());
|
|
||||||
}
|
|
||||||
|
|
||||||
watcher->deleteLater();
|
watcher->deleteLater();
|
||||||
if (next.isPending()) {
|
});
|
||||||
QPromiseFulfill<T>::call(next, res);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
watcher->setFuture(future);
|
watcher->setFuture(future);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
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>;
|
using Watcher = QFutureWatcher<void>;
|
||||||
|
|
||||||
Watcher* watcher = new Watcher();
|
Watcher* watcher = new Watcher();
|
||||||
QObject::connect(
|
QObject::connect(watcher, &Watcher::finished, [=]() mutable {
|
||||||
watcher, &Watcher::finished,
|
try {
|
||||||
[next, watcher]() mutable {
|
// let's rethrown possibe exception
|
||||||
try {
|
watcher->waitForFinished();
|
||||||
// let's rethrown possibe exception
|
resolve();
|
||||||
watcher->waitForFinished();
|
} catch(...) {
|
||||||
} catch(...) {
|
reject(std::current_exception());
|
||||||
next.reject(std::current_exception());
|
}
|
||||||
}
|
|
||||||
|
|
||||||
watcher->deleteLater();
|
watcher->deleteLater();
|
||||||
if (next.isPending()) {
|
});
|
||||||
next.fulfill();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
watcher->setFuture(future);
|
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
|
} // 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
|
// STL
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <array>
|
||||||
|
|
||||||
namespace QtPromisePrivate
|
namespace QtPromisePrivate
|
||||||
{
|
{
|
||||||
@ -13,6 +14,22 @@ namespace QtPromisePrivate
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
using Unqualified = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
|
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
|
* \struct ArgsOf
|
||||||
* http://stackoverflow.com/a/7943765
|
* http://stackoverflow.com/a/7943765
|
||||||
@ -23,6 +40,7 @@ struct ArgsTraits
|
|||||||
{
|
{
|
||||||
using types = std::tuple<Args...>;
|
using types = std::tuple<Args...>;
|
||||||
using first = typename std::tuple_element<0, types>::type;
|
using first = typename std::tuple_element<0, types>::type;
|
||||||
|
static const size_t count = std::tuple_size<types>::value;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
@ -30,14 +48,16 @@ struct ArgsTraits<>
|
|||||||
{
|
{
|
||||||
using types = std::tuple<>;
|
using types = std::tuple<>;
|
||||||
using first = void;
|
using first = void;
|
||||||
|
static const size_t count = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T, typename Enable = void>
|
||||||
struct ArgsOf : public ArgsOf<decltype(&T::operator())>
|
struct ArgsOf : public ArgsTraits<>
|
||||||
{ };
|
{ };
|
||||||
|
|
||||||
template <>
|
template <typename T>
|
||||||
struct ArgsOf<std::nullptr_t> : public ArgsTraits<>
|
struct ArgsOf<T, typename std::enable_if<HasCallOperator<T>::value>::type>
|
||||||
|
: public ArgsOf<decltype(&T::operator())>
|
||||||
{ };
|
{ };
|
||||||
|
|
||||||
template <typename TReturn, typename... Args>
|
template <typename TReturn, typename... Args>
|
||||||
@ -100,25 +120,6 @@ template <typename T>
|
|||||||
struct ArgsOf<const volatile T&&> : public ArgsOf<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
|
} // namespace QtPromisePrivate
|
||||||
|
|
||||||
#endif // ifndef _QTPROMISE_QPROMISEGLOBAL_H
|
#endif // ifndef _QTPROMISE_QPROMISEGLOBAL_H
|
||||||
|
@ -3,4 +3,5 @@ HEADERS += \
|
|||||||
$$PWD/qpromise.inl \
|
$$PWD/qpromise.inl \
|
||||||
$$PWD/qpromise_p.h \
|
$$PWD/qpromise_p.h \
|
||||||
$$PWD/qpromise_qfuture.inl \
|
$$PWD/qpromise_qfuture.inl \
|
||||||
|
$$PWD/qpromiseerror.h \
|
||||||
$$PWD/qpromiseglobal.h
|
$$PWD/qpromiseglobal.h
|
||||||
|
@ -5,15 +5,13 @@
|
|||||||
#include <QtTest>
|
#include <QtTest>
|
||||||
|
|
||||||
using namespace QtPromise;
|
using namespace QtPromise;
|
||||||
|
using namespace QtPromisePrivate;
|
||||||
static const int ASYNC_DELAY = 256;
|
|
||||||
|
|
||||||
class tst_qpromise: public QObject
|
class tst_qpromise: public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
|
|
||||||
void finallyReturns();
|
void finallyReturns();
|
||||||
void finallyThrows();
|
void finallyThrows();
|
||||||
void finallyDelayedFulfilled();
|
void finallyDelayedFulfilled();
|
||||||
@ -26,75 +24,68 @@ QTEST_MAIN(tst_qpromise)
|
|||||||
|
|
||||||
void tst_qpromise::finallyReturns()
|
void tst_qpromise::finallyReturns()
|
||||||
{
|
{
|
||||||
{
|
{ // fulfilled
|
||||||
QPromise<int> p;
|
|
||||||
QVector<int> values;
|
QVector<int> values;
|
||||||
auto next = p.finally([&values]() {
|
auto next = QPromise<int>::resolve(42).finally([&]() {
|
||||||
values << 8;
|
values << 8;
|
||||||
return 16;
|
return 16; // ignored!
|
||||||
});
|
});
|
||||||
|
|
||||||
p.fulfill(42);
|
next.then([&](int r) {
|
||||||
next.then([&values](int r) {
|
|
||||||
values << r;
|
values << r;
|
||||||
}).wait();
|
}).wait();
|
||||||
|
|
||||||
QVERIFY(p.isFulfilled());
|
|
||||||
QVERIFY(next.isFulfilled());
|
QVERIFY(next.isFulfilled());
|
||||||
QCOMPARE(values, QVector<int>({8, 42}));
|
QCOMPARE(values, QVector<int>({8, 42}));
|
||||||
}
|
}
|
||||||
{
|
{ // rejected
|
||||||
QPromise<int> p;
|
QString error;
|
||||||
QVector<int> values;
|
int value = -1;
|
||||||
auto next = p.finally([&values]() {
|
auto next = QPromise<int>([](const QPromiseResolve<int>) {
|
||||||
values << 8;
|
throw QString("foo");
|
||||||
return 16;
|
}).finally([&]() {
|
||||||
|
value = 8;
|
||||||
|
return 16; // ignored!
|
||||||
});
|
});
|
||||||
|
|
||||||
p.reject(QString("foo"));
|
next.fail([&](const QString& err) {
|
||||||
next.then([&values](int r) {
|
error = err;
|
||||||
values << r;
|
return 42;
|
||||||
}).wait();
|
}).wait();
|
||||||
|
|
||||||
QVERIFY(p.isRejected());
|
|
||||||
QVERIFY(next.isRejected());
|
QVERIFY(next.isRejected());
|
||||||
QCOMPARE(values, QVector<int>({8}));
|
QCOMPARE(error, QString("foo"));
|
||||||
|
QCOMPARE(value, 8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void tst_qpromise::finallyThrows()
|
void tst_qpromise::finallyThrows()
|
||||||
{
|
{
|
||||||
{
|
{ // fulfilled
|
||||||
QPromise<int> p;
|
|
||||||
QString error;
|
QString error;
|
||||||
auto next = p.finally([]() {
|
auto next = QPromise<int>::resolve(42).finally([&]() {
|
||||||
throw QString("bar");
|
throw QString("bar");
|
||||||
});
|
});
|
||||||
|
|
||||||
p.fulfill(42);
|
next.fail([&](const QString& err) {
|
||||||
next.fail([&error](const QString& err) {
|
|
||||||
error = err;
|
error = err;
|
||||||
return 0;
|
return 0;
|
||||||
}).wait();
|
}).wait();
|
||||||
|
|
||||||
QVERIFY(p.isFulfilled());
|
|
||||||
QVERIFY(next.isRejected());
|
QVERIFY(next.isRejected());
|
||||||
QCOMPARE(error, QString("bar"));
|
QCOMPARE(error, QString("bar"));
|
||||||
}
|
}
|
||||||
{
|
{ // rejected
|
||||||
QPromise<int> p;
|
|
||||||
QString error;
|
QString error;
|
||||||
auto next = p.finally([]() {
|
auto next = QPromise<int>::reject(QString("foo")).finally([&]() {
|
||||||
throw QString("bar");
|
throw QString("bar");
|
||||||
});
|
});
|
||||||
|
|
||||||
p.reject(QString("foo"));
|
next.fail([&](const QString& err) {
|
||||||
next.fail([&error](const QString& err) {
|
|
||||||
error = err;
|
error = err;
|
||||||
return 0;
|
return 0;
|
||||||
}).wait();
|
}).wait();
|
||||||
|
|
||||||
QVERIFY(p.isRejected());
|
|
||||||
QVERIFY(next.isRejected());
|
QVERIFY(next.isRejected());
|
||||||
QCOMPARE(error, QString("bar"));
|
QCOMPARE(error, QString("bar"));
|
||||||
}
|
}
|
||||||
@ -102,97 +93,89 @@ void tst_qpromise::finallyThrows()
|
|||||||
|
|
||||||
void tst_qpromise::finallyDelayedFulfilled()
|
void tst_qpromise::finallyDelayedFulfilled()
|
||||||
{
|
{
|
||||||
{
|
{ // fulfilled
|
||||||
QPromise<int> p0;
|
|
||||||
QVector<int> values;
|
QVector<int> values;
|
||||||
auto next = p0.finally([&values]() {
|
auto next = QPromise<int>::resolve(42).finally([&]() {
|
||||||
QPromise<int> p1;
|
QPromise<int> p([&](const QPromiseResolve<int>& resolve) {
|
||||||
QTimer::singleShot(ASYNC_DELAY, [p1, &values]() mutable {
|
qtpromise_defer([=, &values]() {
|
||||||
values << 64;
|
values << 64;
|
||||||
p1.fulfill(16);
|
resolve(16); // ignored!
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
values << 8;
|
values << 8;
|
||||||
return p1;
|
return p;
|
||||||
});
|
});
|
||||||
|
|
||||||
p0.fulfill(42);
|
next.then([&](int r) {
|
||||||
next.then([&values](int r) {
|
|
||||||
values << r;
|
values << r;
|
||||||
}).wait();
|
}).wait();
|
||||||
|
|
||||||
QVERIFY(p0.isFulfilled());
|
|
||||||
QVERIFY(next.isFulfilled());
|
QVERIFY(next.isFulfilled());
|
||||||
QCOMPARE(values, QVector<int>({8, 64, 42}));
|
QCOMPARE(values, QVector<int>({8, 64, 42}));
|
||||||
}
|
}
|
||||||
{
|
{ // rejected
|
||||||
QPromise<int> p0;
|
QString error;
|
||||||
QVector<int> values;
|
QVector<int> values;
|
||||||
auto next = p0.finally([&values]() {
|
auto next = QPromise<int>::reject(QString("foo")).finally([&]() {
|
||||||
QPromise<int> p1;
|
QPromise<int> p([&](const QPromiseResolve<int>& resolve) {
|
||||||
QTimer::singleShot(ASYNC_DELAY, [p1, &values]() mutable {
|
qtpromise_defer([=, &values]() {
|
||||||
values << 64;
|
values << 64;
|
||||||
p1.fulfill(16);
|
resolve(16); // ignored!
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
values << 8;
|
values << 8;
|
||||||
return p1;
|
return p;
|
||||||
});
|
});
|
||||||
|
|
||||||
p0.reject(QString("foo"));
|
next.then([&](int r) {
|
||||||
next.then([&values](int r) {
|
|
||||||
values << r;
|
values << r;
|
||||||
|
}, [&](const QString& err) {
|
||||||
|
error = err;
|
||||||
}).wait();
|
}).wait();
|
||||||
|
|
||||||
QVERIFY(p0.isRejected());
|
|
||||||
QVERIFY(next.isRejected());
|
QVERIFY(next.isRejected());
|
||||||
|
QCOMPARE(error, QString("foo"));
|
||||||
QCOMPARE(values, QVector<int>({8, 64}));
|
QCOMPARE(values, QVector<int>({8, 64}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void tst_qpromise::finallyDelayedRejected()
|
void tst_qpromise::finallyDelayedRejected()
|
||||||
{
|
{
|
||||||
{
|
{ // fulfilled
|
||||||
QPromise<int> p0;
|
|
||||||
QString error;
|
QString error;
|
||||||
auto next = p0.finally([]() {
|
auto next = QPromise<int>::resolve(42).finally([]() {
|
||||||
QPromise<int> p1;
|
return QPromise<int>([](const QPromiseResolve<int>&, const QPromiseReject<int>& reject) {
|
||||||
QTimer::singleShot(ASYNC_DELAY, [p1]() mutable {
|
qtpromise_defer([=]() {
|
||||||
p1.reject(QString("bar"));
|
reject(QString("bar"));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return p1;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
p0.fulfill(42);
|
next.fail([&](const QString& err) {
|
||||||
next.fail([&error](const QString& err) {
|
|
||||||
error = err;
|
error = err;
|
||||||
return 0;
|
return 0;
|
||||||
}).wait();
|
}).wait();
|
||||||
|
|
||||||
QVERIFY(p0.isFulfilled());
|
|
||||||
QVERIFY(next.isRejected());
|
QVERIFY(next.isRejected());
|
||||||
QCOMPARE(error, QString("bar"));
|
QCOMPARE(error, QString("bar"));
|
||||||
}
|
}
|
||||||
{
|
{ // rejected
|
||||||
QPromise<int> p0;
|
|
||||||
QString error;
|
QString error;
|
||||||
auto next = p0.finally([]() {
|
auto next = QPromise<int>::reject(QString("foo")).finally([]() {
|
||||||
QPromise<int> p1;
|
return QPromise<int>([](const QPromiseResolve<int>&, const QPromiseReject<int>& reject) {
|
||||||
QTimer::singleShot(ASYNC_DELAY, [p1]() mutable {
|
qtpromise_defer([=]() {
|
||||||
p1.reject(QString("bar"));
|
reject(QString("bar"));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return p1;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
p0.reject(QString("foo"));
|
next.fail([&](const QString& err) {
|
||||||
next.fail([&error](const QString& err) {
|
|
||||||
error = err;
|
error = err;
|
||||||
return 0;
|
return 0;
|
||||||
}).wait();
|
}).wait();
|
||||||
|
|
||||||
QVERIFY(p0.isRejected());
|
|
||||||
QVERIFY(next.isRejected());
|
QVERIFY(next.isRejected());
|
||||||
QCOMPARE(error, QString("bar"));
|
QCOMPARE(error, QString("bar"));
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include <QtTest>
|
#include <QtTest>
|
||||||
|
|
||||||
using namespace QtPromise;
|
using namespace QtPromise;
|
||||||
|
using namespace QtPromisePrivate;
|
||||||
|
|
||||||
// https://promisesaplus.com/#requirements
|
// https://promisesaplus.com/#requirements
|
||||||
class tst_requirements: public QObject
|
class tst_requirements: public QObject
|
||||||
@ -37,115 +38,151 @@ QTEST_MAIN(tst_requirements)
|
|||||||
void tst_requirements::statePending()
|
void tst_requirements::statePending()
|
||||||
{
|
{
|
||||||
// 2.1.1. When pending, a promise:
|
// 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.isPending());
|
||||||
QVERIFY(!p.isFulfilled());
|
QVERIFY(!p.isFulfilled());
|
||||||
QVERIFY(!p.isRejected());
|
QVERIFY(!p.isRejected());
|
||||||
}
|
|
||||||
|
|
||||||
// 2.1.1.1. may transition to either the fulfilled state
|
p.wait();
|
||||||
{
|
|
||||||
QPromise<> p;
|
|
||||||
p.fulfill();
|
|
||||||
QVERIFY(!p.isPending());
|
QVERIFY(!p.isPending());
|
||||||
QVERIFY(p.isFulfilled());
|
QVERIFY(p.isFulfilled());
|
||||||
|
QVERIFY(!p.isRejected());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2.1.1.1. ... or the rejected state
|
// 2.1.1.1. ... or the rejected state
|
||||||
{
|
{
|
||||||
QPromise<> p;
|
QPromise<int> p([&](const QPromiseResolve<int>&, const QPromiseReject<int>& reject) {
|
||||||
p.reject("foo");
|
qtpromise_defer([=]() { reject(QString("foo")); });
|
||||||
|
});
|
||||||
|
|
||||||
|
QVERIFY(p.isPending());
|
||||||
|
QVERIFY(!p.isFulfilled());
|
||||||
|
QVERIFY(!p.isRejected());
|
||||||
|
|
||||||
|
p.wait();
|
||||||
|
|
||||||
QVERIFY(!p.isPending());
|
QVERIFY(!p.isPending());
|
||||||
|
QVERIFY(!p.isFulfilled());
|
||||||
QVERIFY(p.isRejected());
|
QVERIFY(p.isRejected());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void tst_requirements::stateFulfilled()
|
void tst_requirements::stateFulfilled()
|
||||||
{
|
{
|
||||||
QVector<int> values;
|
QString error;
|
||||||
auto log_value = [&values](int res) { values << res; };
|
int value = -1;
|
||||||
|
|
||||||
QPromise<int> p;
|
|
||||||
QVERIFY(p.isPending());
|
|
||||||
|
|
||||||
// 2.1.2. When fulfilled, a promise:
|
// 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.isFulfilled());
|
||||||
QVERIFY(!p.isRejected());
|
QVERIFY(!p.isRejected());
|
||||||
|
QVERIFY(error.isEmpty());
|
||||||
// 2.1.2.1. must not transition to any other state.
|
QCOMPARE(value, 42);
|
||||||
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}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void tst_requirements::stateRejected()
|
void tst_requirements::stateRejected()
|
||||||
{
|
{
|
||||||
QStringList errors;
|
QString error;
|
||||||
auto log_error = [&errors](const QString& err) { errors << err; return -1; };
|
int value = -1;
|
||||||
|
|
||||||
QPromise<int> p;
|
|
||||||
QVERIFY(p.isPending());
|
|
||||||
|
|
||||||
// 2.1.3 When rejected, a promise:
|
// 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.isFulfilled());
|
||||||
QVERIFY(p.isRejected());
|
QVERIFY(p.isRejected());
|
||||||
|
QCOMPARE(error, QString("foo"));
|
||||||
// 2.1.3.1. must not transition to any other state.
|
QCOMPARE(value, -1);
|
||||||
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"}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void tst_requirements::thenArguments()
|
void tst_requirements::thenArguments()
|
||||||
{
|
{
|
||||||
// Both onFulfilled and onRejected are given
|
// 2.2.1. Both onFulfilled and onRejected are given
|
||||||
{
|
{
|
||||||
int value = 0;
|
QString error;
|
||||||
QPromise<int> p;
|
int value = -1;
|
||||||
p.fulfill(42).then([&value](int res) { value = res; }, [](const QString&){}).wait();
|
QPromise<int>::resolve(42).then(
|
||||||
QVERIFY(p.isFulfilled());
|
[&](int res) { value = res; },
|
||||||
|
[&](const QString& err) { error = err; }
|
||||||
|
).wait();
|
||||||
|
|
||||||
|
QVERIFY(error.isEmpty());
|
||||||
QCOMPARE(value, 42);
|
QCOMPARE(value, 42);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
QString error;
|
QString error;
|
||||||
QPromise<int> p;
|
int value = -1;
|
||||||
p.reject(QString("foo")).then([](int) {}, [&error](const QString& err){ error = err; }).wait();
|
QPromise<int>::reject(QString("foo")).then(
|
||||||
QVERIFY(p.isRejected());
|
[&](int res) { value = res; },
|
||||||
|
[&](const QString& err){ error = err; }
|
||||||
|
).wait();
|
||||||
|
|
||||||
QCOMPARE(error, QString("foo"));
|
QCOMPARE(error, QString("foo"));
|
||||||
|
QCOMPARE(value, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2.2.1. onFulfilled is an optional arguments:
|
// 2.2.1. onFulfilled is an optional arguments:
|
||||||
{
|
{
|
||||||
QString error;
|
QString error;
|
||||||
QPromise<int> p;
|
QPromise<int>::reject(QString("foo")).then(
|
||||||
p.reject(QString("foo")).then(nullptr, [&error](const QString& err){ error = err; return 42; }).wait();
|
nullptr,
|
||||||
QVERIFY(p.isRejected());
|
[&](const QString& err){ error = err; return 42; }
|
||||||
|
).wait();
|
||||||
|
|
||||||
QCOMPARE(error, QString("foo"));
|
QCOMPARE(error, QString("foo"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2.2.1. onRejected is an optional arguments:
|
// 2.2.1. onRejected is an optional arguments:
|
||||||
{
|
{
|
||||||
int value = 0;
|
int value = -1;
|
||||||
QPromise<int> p;
|
QPromise<int>::resolve(42).then(
|
||||||
p.fulfill(42).then([&value](int res) { value = res; }).wait();
|
[&value](int res) { value = res; }
|
||||||
QVERIFY(p.isFulfilled());
|
).wait();
|
||||||
|
|
||||||
QCOMPARE(value, 42);
|
QCOMPARE(value, 42);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,55 +194,57 @@ void tst_requirements::thenArguments()
|
|||||||
void tst_requirements::thenOnFulfilled()
|
void tst_requirements::thenOnFulfilled()
|
||||||
{
|
{
|
||||||
// 2.2.2. If onFulfilled is a function:
|
// 2.2.2. If onFulfilled is a function:
|
||||||
|
|
||||||
QPromise<int> p0;
|
|
||||||
QVector<int> values;
|
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.
|
// 2.2.2.2. it must not be called before promise is fulfilled.
|
||||||
QVERIFY(p0.isPending());
|
QVERIFY(p0.isPending());
|
||||||
QVERIFY(p1.isPending());
|
QVERIFY(p1.isPending());
|
||||||
QVERIFY(values.isEmpty());
|
QVERIFY(values.isEmpty());
|
||||||
|
|
||||||
|
p1.wait();
|
||||||
|
|
||||||
// 2.2.2.1. it must be called after promise is fulfilled,
|
// 2.2.2.1. it must be called after promise is fulfilled,
|
||||||
// with promise’s value as its first argument.
|
// with promise’s value as its first argument.
|
||||||
p0.fulfill(42);
|
|
||||||
p1.wait();
|
|
||||||
QVERIFY(p0.isFulfilled());
|
QVERIFY(p0.isFulfilled());
|
||||||
QVERIFY(p1.isFulfilled());
|
QVERIFY(p1.isFulfilled());
|
||||||
QCOMPARE(values, QVector<int>({42}));
|
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()
|
void tst_requirements::thenOnRejected()
|
||||||
{
|
{
|
||||||
// 2.2.3. If onRejected is a function:
|
// 2.2.3. If onRejected is a function:
|
||||||
|
|
||||||
QPromise<> p0;
|
|
||||||
QStringList errors;
|
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.
|
// 2.2.3.2. it must not be called before promise is rejected.
|
||||||
QVERIFY(p0.isPending());
|
QVERIFY(p0.isPending());
|
||||||
QVERIFY(p1.isPending());
|
QVERIFY(p1.isPending());
|
||||||
QVERIFY(errors.isEmpty());
|
QVERIFY(errors.isEmpty());
|
||||||
|
|
||||||
|
p1.wait();
|
||||||
|
|
||||||
// 2.2.3.1. it must be called after promise is rejected,
|
// 2.2.3.1. it must be called after promise is rejected,
|
||||||
// with promise’s reason as its first argument.
|
// with promise’s reason as its first argument.
|
||||||
p0.reject(QString("foo"));
|
|
||||||
p1.wait();
|
|
||||||
QVERIFY(p0.isRejected());
|
QVERIFY(p0.isRejected());
|
||||||
QVERIFY(p1.isFulfilled());
|
QVERIFY(p1.isFulfilled());
|
||||||
QCOMPARE(errors, QStringList({"foo"}));
|
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()
|
void tst_requirements::thenAsynchronous()
|
||||||
@ -214,14 +253,13 @@ void tst_requirements::thenAsynchronous()
|
|||||||
// stack contains only platform code (ie. executed asynchronously, after the event
|
// stack contains only platform code (ie. executed asynchronously, after the event
|
||||||
// loop turn in which then is called, and with a fresh stack).
|
// loop turn in which then is called, and with a fresh stack).
|
||||||
|
|
||||||
int value = 0;
|
int value = -1;
|
||||||
QPromise<int> p0;
|
auto p0 = QPromise<int>::resolve(42);
|
||||||
p0.fulfill(42);
|
|
||||||
QVERIFY(p0.isFulfilled());
|
QVERIFY(p0.isFulfilled());
|
||||||
|
|
||||||
auto p1 = p0.then([&value](int res){ value = res; });
|
auto p1 = p0.then([&](int res){ value = res; });
|
||||||
QVERIFY(p1.isPending());
|
QVERIFY(p1.isPending());
|
||||||
QCOMPARE(value, 0);
|
QCOMPARE(value, -1);
|
||||||
|
|
||||||
p1.wait();
|
p1.wait();
|
||||||
QVERIFY(p1.isFulfilled());
|
QVERIFY(p1.isFulfilled());
|
||||||
@ -235,32 +273,38 @@ void tst_requirements::thenMultipleCalls()
|
|||||||
// 2.2.6.1. If/when promise is fulfilled, all respective onFulfilled callbacks
|
// 2.2.6.1. If/when promise is fulfilled, all respective onFulfilled callbacks
|
||||||
// must execute in the order of their originating calls to then:
|
// must execute in the order of their originating calls to then:
|
||||||
{
|
{
|
||||||
QPromise<int> p;
|
|
||||||
QVector<int> values;
|
QVector<int> values;
|
||||||
auto all = qPromiseAll(QVector<QPromise<void> >{
|
QPromise<int> p([](const QPromiseResolve<int>& resolve) {
|
||||||
p.then([&values](int r) { values << r + 1; }),
|
qtpromise_defer([=]() {
|
||||||
p.then([&values](int r) { values << r + 2; }),
|
resolve(42);
|
||||||
p.then([&values](int r) { values << r + 3; })
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
p.fulfill(42);
|
qPromiseAll(QVector<QPromise<void> >{
|
||||||
all.wait();
|
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}));
|
QCOMPARE(values, QVector<int>({43, 44, 45}));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2.2.6.2. If/when promise is rejected, all respective onRejected callbacks
|
// 2.2.6.2. If/when promise is rejected, all respective onRejected callbacks
|
||||||
// must execute in the order of their originating calls to then:
|
// must execute in the order of their originating calls to then:
|
||||||
{
|
{
|
||||||
QPromise<int> p;
|
|
||||||
QVector<int> values;
|
QVector<int> values;
|
||||||
auto all = qPromiseAll(QVector<QPromise<int> >{
|
QPromise<int> p([](const QPromiseResolve<int>&, const QPromiseReject<int>& reject) {
|
||||||
p.then(nullptr, [&values](int r) { values << r + 1; return r + 1; }),
|
qtpromise_defer([=]() {
|
||||||
p.then(nullptr, [&values](int r) { values << r + 2; return r + 2; }),
|
reject(8);
|
||||||
p.then(nullptr, [&values](int r) { values << r + 3; return r + 3; })
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
p.reject(8);
|
qPromiseAll(QVector<QPromise<int> >{
|
||||||
all.wait();
|
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}));
|
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);
|
// 2.2.7. then must return a promise: p2 = p1.then(onFulfilled, onRejected);
|
||||||
{
|
{
|
||||||
QPromise<int> p1;
|
|
||||||
auto handler = [](){ return 42; };
|
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(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(nullptr, handler)), QPromise<int> >::value));
|
||||||
Q_STATIC_ASSERT((std::is_same<decltype(p1.then(handler, 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.
|
// p2 must be rejected with e as the reason.
|
||||||
{
|
{
|
||||||
QString reason;
|
QString reason;
|
||||||
QPromise<int> p1;
|
auto p1 = QPromise<int>::resolve(42);
|
||||||
auto p2 = p1.fulfill(42).then([](){ throw QString("foo"); });
|
auto p2 = p1.then([](){ throw QString("foo"); });
|
||||||
p2.then(nullptr, [&reason](const QString& e) { reason = e; }).wait();
|
p2.then(nullptr, [&](const QString& e) { reason = e; }).wait();
|
||||||
|
|
||||||
|
QVERIFY(p1.isFulfilled());
|
||||||
QVERIFY(p2.isRejected());
|
QVERIFY(p2.isRejected());
|
||||||
QCOMPARE(reason, QString("foo"));
|
QCOMPARE(reason, QString("foo"));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
QString reason;
|
QString reason;
|
||||||
QPromise<int> p1;
|
auto p1 = QPromise<int>::reject(QString("foo"));
|
||||||
auto p2 = p1.reject(QString("foo")).then(nullptr, [](){ throw QString("bar"); return 42; });
|
auto p2 = p1.then(nullptr, [](){ throw QString("bar"); return 42; });
|
||||||
p2.then(nullptr, [&reason](const QString& e) { reason = e; return 0; }).wait();
|
p2.then(nullptr, [&](const QString& e) { reason = e; return 0; }).wait();
|
||||||
|
|
||||||
|
QVERIFY(p1.isRejected());
|
||||||
QVERIFY(p2.isRejected());
|
QVERIFY(p2.isRejected());
|
||||||
QCOMPARE(reason, QString("bar"));
|
QCOMPARE(reason, QString("bar"));
|
||||||
}
|
}
|
||||||
@ -304,11 +350,12 @@ void tst_requirements::thenHandlers()
|
|||||||
// p2 must be fulfilled with the same value as promise1
|
// p2 must be fulfilled with the same value as promise1
|
||||||
{
|
{
|
||||||
QString value;
|
QString value;
|
||||||
QPromise<QString> p1;
|
auto p1 = QPromise<QString>::resolve("42");
|
||||||
auto p2 = p1.fulfill("42").then(nullptr, [](){ return QString(); });
|
auto p2 = p1.then(nullptr, [](){ return QString(); });
|
||||||
Q_STATIC_ASSERT((std::is_same<decltype(p2), QPromise<QString> >::value));
|
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());
|
QVERIFY(p2.isFulfilled());
|
||||||
QCOMPARE(value, QString("42"));
|
QCOMPARE(value, QString("42"));
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user