mirror of
https://github.com/simonbrunel/qtpromise.git
synced 2024-11-23 19:20:57 +08:00
- wip -
This commit is contained in:
parent
d279fb4391
commit
50216b65da
1
.gitignore
vendored
1
.gitignore
vendored
@ -8,6 +8,7 @@ node_modules
|
|||||||
*.obj
|
*.obj
|
||||||
*.exe
|
*.exe
|
||||||
*.user
|
*.user
|
||||||
|
*.qmlc
|
||||||
Makefile*
|
Makefile*
|
||||||
moc_*.cpp
|
moc_*.cpp
|
||||||
moc_*.h
|
moc_*.h
|
||||||
|
6
include/QtQmlPromise
Normal file
6
include/QtQmlPromise
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#ifndef QTQMLPROMISE_MODULE_H
|
||||||
|
#define QTQMLPROMISE_MODULE_H
|
||||||
|
|
||||||
|
#include "../src/qtqmlpromise/qjspromise.h"
|
||||||
|
|
||||||
|
#endif // ifndef QTQMLPROMISE_MODULE_H
|
@ -1,3 +1,11 @@
|
|||||||
INCLUDEPATH += $$PWD/include $$PWD/src
|
INCLUDEPATH += $$PWD/include $$PWD/src
|
||||||
DEPENDPATH += $$PWD/include $$PWD/src
|
DEPENDPATH += $$PWD/include $$PWD/src
|
||||||
CONFIG += c++11
|
CONFIG += c++11
|
||||||
|
|
||||||
|
qtpromise-qml {
|
||||||
|
QML_IMPORT_PATH += $$shadowed($$PWD)/qml
|
||||||
|
|
||||||
|
# To avoid carrying an extra library dependency, the QJSPromise definition is
|
||||||
|
# embedded in the QML plugin, so we need to link against the plugin itself.
|
||||||
|
LIBS += -L$$shadowed($$PWD)/qml/QtPromise -l$$qtLibraryTarget(qtpromiseplugin)
|
||||||
|
}
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
TEMPLATE = subdirs
|
TEMPLATE = subdirs
|
||||||
SUBDIRS = \
|
SUBDIRS = \
|
||||||
|
src \
|
||||||
tests
|
tests
|
||||||
|
|
||||||
_qt_creator_ {
|
tests.depends = src
|
||||||
SUBDIRS += src
|
|
||||||
}
|
|
||||||
|
|
||||||
OTHER_FILES = \
|
OTHER_FILES = \
|
||||||
package/features/*.prf \
|
package/features/*.prf \
|
||||||
|
25
src/imports/imports.pro
Normal file
25
src/imports/imports.pro
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
TEMPLATE = lib
|
||||||
|
CONFIG += plugin exceptions
|
||||||
|
QT += qml
|
||||||
|
|
||||||
|
IMPORT_VERSION = 1.0
|
||||||
|
DEFINES += QTQMLPROMISE_LIBRARY
|
||||||
|
|
||||||
|
TARGET = $$qtLibraryTarget(qtpromiseplugin)
|
||||||
|
DESTDIR = $$shadowed($$PWD/../../qml/QtPromise)
|
||||||
|
|
||||||
|
include(../qtqmlpromise/qtqmlpromise.pri)
|
||||||
|
include(../../qtpromise.pri)
|
||||||
|
|
||||||
|
SOURCES += \
|
||||||
|
$$PWD/plugin.cpp
|
||||||
|
|
||||||
|
QMLFILES += \
|
||||||
|
$$PWD/plugins.qmltypes \
|
||||||
|
$$PWD/qmldir
|
||||||
|
|
||||||
|
RESOURCES += $$PWD/imports.qrc
|
||||||
|
|
||||||
|
qmlfiles.files = $$QMLFILES
|
||||||
|
qmlfiles.path = $$DESTDIR
|
||||||
|
COPIES += qmlfiles
|
5
src/imports/imports.qrc
Normal file
5
src/imports/imports.qrc
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<RCC>
|
||||||
|
<qresource prefix="/QtPromise">
|
||||||
|
<file>qtqmlpromise.js</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
76
src/imports/plugin.cpp
Normal file
76
src/imports/plugin.cpp
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
// QtPromise
|
||||||
|
#include <QtQmlPromise>
|
||||||
|
|
||||||
|
// Qt
|
||||||
|
#include <QQmlEngine>
|
||||||
|
#include <QQmlExtensionPlugin>
|
||||||
|
|
||||||
|
static const char* kJSPromisePrivateNamespace = "__qtpromise_private__";
|
||||||
|
|
||||||
|
using namespace QtPromise;
|
||||||
|
|
||||||
|
class QtQmlPromiseObject : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
QtQmlPromiseObject(QJSEngine* engine)
|
||||||
|
: QObject(engine)
|
||||||
|
, m_engine(engine)
|
||||||
|
{
|
||||||
|
Q_ASSERT(engine);
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_INVOKABLE QJSValue create(const QJSValue& resolver, const QJSValue& prototype)
|
||||||
|
{
|
||||||
|
QJSValue value = m_engine->toScriptValue(QJSPromise(m_engine, resolver));
|
||||||
|
value.setPrototype(prototype);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_INVOKABLE QtPromise::QJSPromise resolve(QJSValue value)
|
||||||
|
{
|
||||||
|
return QJSPromise::resolve(std::move(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_INVOKABLE QtPromise::QJSPromise reject(QJSValue error)
|
||||||
|
{
|
||||||
|
return QJSPromise::reject(std::move(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_INVOKABLE QtPromise::QJSPromise all(QJSValue input)
|
||||||
|
{
|
||||||
|
return QJSPromise::all(m_engine, std::move(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QJSEngine* m_engine;
|
||||||
|
};
|
||||||
|
|
||||||
|
class QtQmlPromisePlugin : public QQmlExtensionPlugin
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
|
||||||
|
|
||||||
|
public:
|
||||||
|
void registerTypes(const char* uri) Q_DECL_OVERRIDE
|
||||||
|
{
|
||||||
|
Q_ASSERT(QLatin1String(uri) == QLatin1String("QtPromise"));
|
||||||
|
Q_UNUSED(uri);
|
||||||
|
|
||||||
|
qRegisterMetaType<QJSPromise>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void initializeEngine(QQmlEngine* engine, const char* uri) Q_DECL_OVERRIDE
|
||||||
|
{
|
||||||
|
Q_ASSERT(QLatin1String(uri) == QLatin1String("QtPromise"));
|
||||||
|
Q_UNUSED(uri);
|
||||||
|
|
||||||
|
QJSValue global = engine->globalObject();
|
||||||
|
QJSValue object = engine->newQObject(new QtQmlPromiseObject(engine));
|
||||||
|
global.setProperty(kJSPromisePrivateNamespace, object);
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // class QtQmlPromisePlugin
|
||||||
|
|
||||||
|
#include "plugin.moc"
|
5
src/imports/plugins.qmltypes
Normal file
5
src/imports/plugins.qmltypes
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import QtQuick.tooling 1.2
|
||||||
|
|
||||||
|
Module {
|
||||||
|
dependencies: []
|
||||||
|
}
|
5
src/imports/qmldir
Normal file
5
src/imports/qmldir
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
module QtPromise
|
||||||
|
plugin qtpromiseplugin
|
||||||
|
typeinfo plugins.qmltypes
|
||||||
|
classname QtQmlPromisePlugin
|
||||||
|
Promise 1.0 qrc:///QtPromise/qtqmlpromise.js
|
16
src/imports/qtqmlpromise.js
Normal file
16
src/imports/qtqmlpromise.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
.pragma library
|
||||||
|
|
||||||
|
(function(global) {
|
||||||
|
var private = global.__qtpromise_private__;
|
||||||
|
delete global.__qtpromise_private__;
|
||||||
|
|
||||||
|
var Promise = global.Promise = function(resolver) {
|
||||||
|
return private.create(function(proxy) {
|
||||||
|
resolver(proxy.resolve, proxy.reject);
|
||||||
|
}, this);
|
||||||
|
};
|
||||||
|
|
||||||
|
['all', 'reject', 'resolve'].forEach(function(method) {
|
||||||
|
Promise[method] = private[method];
|
||||||
|
});
|
||||||
|
})(this);
|
@ -1,5 +1,5 @@
|
|||||||
TEMPLATE = lib
|
TEMPLATE = aux
|
||||||
CONFIG += c++11 qt thread warn_on
|
CONFIG += c++11 force_qt thread warn_on
|
||||||
DEFINES += QT_DEPRECATED_WARNINGS
|
DEFINES += QT_DEPRECATED_WARNINGS
|
||||||
|
|
||||||
include(qtpromise.pri)
|
include(qtpromise.pri)
|
||||||
|
175
src/qtqmlpromise/qjspromise.cpp
Normal file
175
src/qtqmlpromise/qjspromise.cpp
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
// QtQmlPromise
|
||||||
|
#include "qjspromise.h"
|
||||||
|
#include "qjspromise_p.h"
|
||||||
|
|
||||||
|
// Qt
|
||||||
|
#include <QJSValueIterator>
|
||||||
|
#include <QJSEngine>
|
||||||
|
|
||||||
|
using namespace QtPromise;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
QJSValue toJSArray(QJSEngine* engine, const QVector<QJSValue>& values)
|
||||||
|
{
|
||||||
|
Q_ASSERT(engine);
|
||||||
|
|
||||||
|
const int count = values.count();
|
||||||
|
QJSValue array = engine->newArray(count);
|
||||||
|
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
array.setProperty(i, values[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
QJSValue newError(const QJSValue& source, const T& value)
|
||||||
|
{
|
||||||
|
QJSEngine* engine = source.engine();
|
||||||
|
QJSValue error = engine->evaluate("throw new Error('')");
|
||||||
|
error.setProperty("message", engine->toScriptValue(value));
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
QJSPromise::QJSPromise()
|
||||||
|
: m_promise(QPromise<QJSValue>::resolve(QJSValue()))
|
||||||
|
{ }
|
||||||
|
|
||||||
|
QJSPromise::QJSPromise(QPromise<QJSValue>&& promise)
|
||||||
|
: m_promise(std::move(promise))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QJSPromise::QJSPromise(QJSEngine* engine, QJSValue resolver)
|
||||||
|
: m_promise([=](
|
||||||
|
const QPromiseResolve<QJSValue>& resolve,
|
||||||
|
const QPromiseReject<QJSValue>& reject) mutable {
|
||||||
|
|
||||||
|
// resolver is part of the Promise wrapper in qtqmlpromise.js
|
||||||
|
Q_ASSERT(resolver.isCallable());
|
||||||
|
Q_ASSERT(engine);
|
||||||
|
|
||||||
|
auto proxy = QtPromisePrivate::JSPromiseResolver(resolve, reject);
|
||||||
|
auto ret = resolver.call(QJSValueList() << engine->toScriptValue(proxy));
|
||||||
|
if (ret.isError()) {
|
||||||
|
throw ret;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
{ }
|
||||||
|
|
||||||
|
QJSPromise QJSPromise::then(QJSValue fulfilled, QJSValue rejected) const
|
||||||
|
{
|
||||||
|
const bool fulfillable = fulfilled.isCallable();
|
||||||
|
const bool rejectable = rejected.isCallable();
|
||||||
|
|
||||||
|
if (!fulfillable && !rejectable) {
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto _rejected = [=]() mutable {
|
||||||
|
QJSValue error;
|
||||||
|
|
||||||
|
try {
|
||||||
|
throw;
|
||||||
|
} catch (const QJSValue& err) {
|
||||||
|
error = err;
|
||||||
|
} catch (const QVariant& err) {
|
||||||
|
error = newError(rejected, err);
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
error = newError(rejected, QString(e.what()));
|
||||||
|
} catch (...) {
|
||||||
|
error = newError(rejected, QString("Unknown error"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return rejected.call({error});
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!fulfillable) {
|
||||||
|
return m_promise.then(nullptr, _rejected);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto _fulfilled = [=](const QJSValue& res) mutable {
|
||||||
|
return fulfilled.call(QJSValueList() << res);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!rejectable) {
|
||||||
|
return m_promise.then(_fulfilled);
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_promise.then(_fulfilled, _rejected);
|
||||||
|
}
|
||||||
|
|
||||||
|
QJSPromise QJSPromise::fail(QJSValue handler) const
|
||||||
|
{
|
||||||
|
return then(QJSValue(), handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
QJSPromise QJSPromise::finally(QJSValue handler) const
|
||||||
|
{
|
||||||
|
return m_promise.finally([=]() mutable {
|
||||||
|
return handler.call();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QJSPromise QJSPromise::tap(QJSValue handler) const
|
||||||
|
{
|
||||||
|
return m_promise.tap([=](const QJSValue& res) mutable {
|
||||||
|
return handler.call(QJSValueList() << res);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QJSPromise QJSPromise::delay(int msec) const
|
||||||
|
{
|
||||||
|
return m_promise.delay(msec);
|
||||||
|
}
|
||||||
|
|
||||||
|
QJSPromise QJSPromise::wait() const
|
||||||
|
{
|
||||||
|
return m_promise.wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
QJSPromise QJSPromise::resolve(QJSValue&& value)
|
||||||
|
{
|
||||||
|
return QPromise<QJSValue>::resolve(std::forward<QJSValue>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
QJSPromise QJSPromise::reject(QJSValue&& error)
|
||||||
|
{
|
||||||
|
return QPromise<QJSValue>::reject(std::forward<QJSValue>(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
QJSPromise QJSPromise::all(QJSEngine* engine, QJSValue&& input)
|
||||||
|
{
|
||||||
|
Q_ASSERT(engine);
|
||||||
|
|
||||||
|
if (!input.isArray()) {
|
||||||
|
// TODO TYPEERROR!
|
||||||
|
return QPromise<QJSValue>::reject("foobar");
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_ASSERT(input.hasProperty("length"));
|
||||||
|
const int count = input.property("length").toInt();
|
||||||
|
if (!count) {
|
||||||
|
return QPromise<QJSValue>::resolve(QJSValue(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QPromise<QJSValue> > promises;
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
QJSValue value = input.property(i);
|
||||||
|
const QVariant variant = value.toVariant();
|
||||||
|
if (variant.userType() == qMetaTypeId<QJSPromise>()) {
|
||||||
|
promises << variant.value<QJSPromise>().m_promise;
|
||||||
|
} else {
|
||||||
|
promises << QPromise<QJSValue>::resolve(std::move(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return QPromise<QJSValue>::all(promises)
|
||||||
|
.then([engine](const QVector<QJSValue>& results) {
|
||||||
|
return toJSArray(engine, results);
|
||||||
|
});
|
||||||
|
}
|
52
src/qtqmlpromise/qjspromise.h
Normal file
52
src/qtqmlpromise/qjspromise.h
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
#ifndef QTQMLPROMISE_QJSPROMISE_H
|
||||||
|
#define QTQMLPROMISE_QJSPROMISE_H
|
||||||
|
|
||||||
|
// QtQmlPromise
|
||||||
|
#include "qtqmlpromiseglobal.h"
|
||||||
|
|
||||||
|
// QtPromise
|
||||||
|
#include <QtPromise>
|
||||||
|
|
||||||
|
// Qt
|
||||||
|
#include <QJSValue>
|
||||||
|
|
||||||
|
namespace QtPromise {
|
||||||
|
|
||||||
|
class QTQMLPROMISE_EXPORT QJSPromise
|
||||||
|
{
|
||||||
|
Q_GADGET
|
||||||
|
|
||||||
|
public:
|
||||||
|
QJSPromise();
|
||||||
|
QJSPromise(QJSEngine* engine, QJSValue resolver);
|
||||||
|
QJSPromise(QPromise<QJSValue>&& promise);
|
||||||
|
|
||||||
|
Q_INVOKABLE bool isFulfilled() const { return m_promise.isFulfilled(); }
|
||||||
|
Q_INVOKABLE bool isRejected() const { return m_promise.isRejected(); }
|
||||||
|
Q_INVOKABLE bool isPending() const{ return m_promise.isPending(); }
|
||||||
|
|
||||||
|
Q_INVOKABLE QtPromise::QJSPromise then(QJSValue fulfilled, QJSValue rejected = QJSValue()) const;
|
||||||
|
Q_INVOKABLE QtPromise::QJSPromise fail(QJSValue handler) const;
|
||||||
|
Q_INVOKABLE QtPromise::QJSPromise finally(QJSValue handler) const;
|
||||||
|
Q_INVOKABLE QtPromise::QJSPromise tap(QJSValue handler) const;
|
||||||
|
Q_INVOKABLE QtPromise::QJSPromise delay(int msec) const;
|
||||||
|
Q_INVOKABLE QtPromise::QJSPromise wait() const;
|
||||||
|
|
||||||
|
public: // STATICS
|
||||||
|
static QJSPromise resolve(QJSValue&& value);
|
||||||
|
static QJSPromise reject(QJSValue&& error);
|
||||||
|
static QJSPromise all(QJSEngine* engine, QJSValue&& input);
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend struct QtPromisePrivate::PromiseFulfill<QJSValue>;
|
||||||
|
|
||||||
|
QPromise<QJSValue> m_promise;
|
||||||
|
|
||||||
|
}; // class QJSPromise
|
||||||
|
|
||||||
|
} // namespace QtPromise
|
||||||
|
|
||||||
|
Q_DECLARE_TYPEINFO(QtPromise::QJSPromise, Q_MOVABLE_TYPE);
|
||||||
|
Q_DECLARE_METATYPE(QtPromise::QJSPromise)
|
||||||
|
|
||||||
|
#endif // ifndef QTQMLPROMISE_QJSPROMISE_H
|
71
src/qtqmlpromise/qjspromise_p.h
Normal file
71
src/qtqmlpromise/qjspromise_p.h
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
#ifndef QTQMLPROMISE_QJSPROMISE_P_H
|
||||||
|
#define QTQMLPROMISE_QJSPROMISE_P_H
|
||||||
|
|
||||||
|
// QtQmlPromise
|
||||||
|
#include "qjspromise.h"
|
||||||
|
|
||||||
|
// QtPromise
|
||||||
|
#include <QtPromise>
|
||||||
|
|
||||||
|
// Qt
|
||||||
|
#include <QJSValue>
|
||||||
|
|
||||||
|
namespace QtPromisePrivate {
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct PromiseFulfill<QJSValue>
|
||||||
|
{
|
||||||
|
static void call(
|
||||||
|
const QJSValue& value,
|
||||||
|
const QtPromise::QPromiseResolve<QJSValue>& resolve,
|
||||||
|
const QtPromise::QPromiseReject<QJSValue>& reject)
|
||||||
|
{
|
||||||
|
using namespace QtPromise;
|
||||||
|
|
||||||
|
if (value.isObject()) {
|
||||||
|
const QVariant variant = value.toVariant();
|
||||||
|
if (variant.userType() == qMetaTypeId<QJSPromise>()) {
|
||||||
|
const auto promise = variant.value<QJSPromise>().m_promise;
|
||||||
|
PromiseFulfill<QPromise<QJSValue> >::call(promise, resolve, reject);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.isError()) {
|
||||||
|
reject(value);
|
||||||
|
} else {
|
||||||
|
resolve(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class JSPromiseResolver
|
||||||
|
{
|
||||||
|
Q_GADGET
|
||||||
|
|
||||||
|
public:
|
||||||
|
JSPromiseResolver() {}
|
||||||
|
JSPromiseResolver(
|
||||||
|
const QtPromise::QPromiseResolve<QJSValue>& resolve,
|
||||||
|
const QtPromise::QPromiseReject<QJSValue>& reject)
|
||||||
|
: m_d(new Data{resolve, reject})
|
||||||
|
{ }
|
||||||
|
|
||||||
|
Q_INVOKABLE void resolve(QJSValue value = QJSValue()) { m_d->resolve(std::move(value)); }
|
||||||
|
Q_INVOKABLE void reject(QJSValue error = QJSValue()) { m_d->reject(std::move(error)); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Data
|
||||||
|
{
|
||||||
|
QtPromise::QPromiseResolve<QJSValue> resolve;
|
||||||
|
QtPromise::QPromiseReject<QJSValue> reject;
|
||||||
|
}; // struct Data
|
||||||
|
|
||||||
|
QSharedPointer<Data> m_d;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QtPromisePrivate
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(QtPromisePrivate::JSPromiseResolver)
|
||||||
|
|
||||||
|
#endif // ifndef QTQMLPROMISE_QJSPROMISE_P_H
|
7
src/qtqmlpromise/qtqmlpromise.pri
Normal file
7
src/qtqmlpromise/qtqmlpromise.pri
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
HEADERS += \
|
||||||
|
$$PWD/qjspromise.h \
|
||||||
|
$$PWD/qjspromise_p.h \
|
||||||
|
$$PWD/qtqmlpromiseglobal.h
|
||||||
|
|
||||||
|
SOURCES += \
|
||||||
|
$$PWD/qjspromise.cpp
|
8
src/qtqmlpromise/qtqmlpromise.pro
Normal file
8
src/qtqmlpromise/qtqmlpromise.pro
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
TEMPLATE = aux
|
||||||
|
CONFIG += c++11 force_qt thread warn_on
|
||||||
|
DEFINES += QT_DEPRECATED_WARNINGS
|
||||||
|
DEFINES += QTQMLPROMISE_LIBRARY
|
||||||
|
QT += qml
|
||||||
|
|
||||||
|
include($$PWD/../../qtpromise.pri)
|
||||||
|
include($$PWD/qtqmlpromise.pri)
|
16
src/qtqmlpromise/qtqmlpromiseglobal.h
Normal file
16
src/qtqmlpromise/qtqmlpromiseglobal.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#ifndef QTQMLPROMISE_QTQMLPROMISEGLOBAL_H
|
||||||
|
#define QTQMLPROMISE_QTQMLPROMISEGLOBAL_H
|
||||||
|
|
||||||
|
// QtPromise
|
||||||
|
#include <QtPromise>
|
||||||
|
|
||||||
|
// QtCore
|
||||||
|
#include <QtGlobal>
|
||||||
|
|
||||||
|
#ifdef QTQMLPROMISE_LIBRARY
|
||||||
|
# define QTQMLPROMISE_EXPORT Q_DECL_EXPORT
|
||||||
|
#else
|
||||||
|
# define QTQMLPROMISE_EXPORT Q_DECL_IMPORT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // ifndef QTQMLPROMISE_QTQMLPROMISEGLOBAL_H
|
@ -1,2 +1,5 @@
|
|||||||
TEMPLATE = subdirs
|
TEMPLATE = subdirs
|
||||||
SUBDIRS = qtpromise
|
SUBDIRS += \
|
||||||
|
imports \
|
||||||
|
qtpromise \
|
||||||
|
qtqmlpromise
|
||||||
|
@ -1,2 +1,4 @@
|
|||||||
TEMPLATE = subdirs
|
TEMPLATE = subdirs
|
||||||
SUBDIRS += qtpromise
|
SUBDIRS += \
|
||||||
|
qtpromise \
|
||||||
|
qtqmlpromise
|
||||||
|
5
tests/auto/qtqmlpromise/extension/extension.pro
Normal file
5
tests/auto/qtqmlpromise/extension/extension.pro
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
TARGET = tst_qtqmlpromise_extension
|
||||||
|
SOURCES += $$PWD/tst_extension.cpp
|
||||||
|
OTHER_FILES += $$PWD/*.qml
|
||||||
|
|
||||||
|
include(../qtqmlpromise.pri)
|
10
tests/auto/qtqmlpromise/extension/tst_extension.cpp
Normal file
10
tests/auto/qtqmlpromise/extension/tst_extension.cpp
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#include <QtQuickTest/quicktest.h>
|
||||||
|
|
||||||
|
static void initialize()
|
||||||
|
{
|
||||||
|
qputenv("QML2_IMPORT_PATH", QTPROMISE_IMPORTPATH);
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_COREAPP_STARTUP_FUNCTION(initialize)
|
||||||
|
|
||||||
|
QUICK_TEST_MAIN(extension)
|
39
tests/auto/qtqmlpromise/extension/tst_extension.qml
Normal file
39
tests/auto/qtqmlpromise/extension/tst_extension.qml
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import QtQuick 2.3
|
||||||
|
import QtPromise 1.0
|
||||||
|
import QtTest 1.0
|
||||||
|
|
||||||
|
TestCase {
|
||||||
|
name: "Extension"
|
||||||
|
|
||||||
|
function test_global() {
|
||||||
|
compare(typeof __qtpromise_private__, 'undefined');
|
||||||
|
compare(typeof Promise, 'function');
|
||||||
|
compare(typeof Promise.resolve, 'function');
|
||||||
|
compare(typeof Promise.reject, 'function');
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_instance() {
|
||||||
|
var p = new Promise(function() {});
|
||||||
|
compare(Object.prototype.toString(p), '[object Object]');
|
||||||
|
compare(p instanceof Promise, true);
|
||||||
|
compare(typeof p, 'object');
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_prototype() {
|
||||||
|
var p = new Promise(function() {});
|
||||||
|
|
||||||
|
[
|
||||||
|
'delay',
|
||||||
|
'fail',
|
||||||
|
'finally',
|
||||||
|
'isFulfilled',
|
||||||
|
'isRejected',
|
||||||
|
'isPending',
|
||||||
|
'tap',
|
||||||
|
'then',
|
||||||
|
'wait',
|
||||||
|
].forEach(function(name) {
|
||||||
|
compare(typeof p[name], 'function');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
9
tests/auto/qtqmlpromise/qtqmlpromise.pri
Normal file
9
tests/auto/qtqmlpromise/qtqmlpromise.pri
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
TEMPLATE = app
|
||||||
|
CONFIG += qmltestcase
|
||||||
|
CONFIG += qtpromise-qml
|
||||||
|
DEFINES += QT_DEPRECATED_WARNINGS
|
||||||
|
|
||||||
|
QTPROMISE_IMPORTPATH = $$shadowed($$PWD/../../../qml)
|
||||||
|
DEFINES += QTPROMISE_IMPORTPATH=\"\\\"$$QTPROMISE_IMPORTPATH\\\"\"
|
||||||
|
|
||||||
|
include(../../../qtpromise.pri)
|
4
tests/auto/qtqmlpromise/qtqmlpromise.pro
Normal file
4
tests/auto/qtqmlpromise/qtqmlpromise.pro
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
TEMPLATE = subdirs
|
||||||
|
SUBDIRS += \
|
||||||
|
extension \
|
||||||
|
requirements
|
5
tests/auto/qtqmlpromise/requirements/requirements.pro
Normal file
5
tests/auto/qtqmlpromise/requirements/requirements.pro
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
TARGET = tst_qtqmlpromise_requirements
|
||||||
|
SOURCES += $$PWD/tst_requirements.cpp
|
||||||
|
OTHER_FILES += $$PWD/*.qml
|
||||||
|
|
||||||
|
include(../qtqmlpromise.pri)
|
10
tests/auto/qtqmlpromise/requirements/tst_requirements.cpp
Normal file
10
tests/auto/qtqmlpromise/requirements/tst_requirements.cpp
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#include <QtQuickTest/quicktest.h>
|
||||||
|
|
||||||
|
static void initialize()
|
||||||
|
{
|
||||||
|
qputenv("QML2_IMPORT_PATH", QTPROMISE_IMPORTPATH);
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_COREAPP_STARTUP_FUNCTION(initialize)
|
||||||
|
|
||||||
|
QUICK_TEST_MAIN(requirements)
|
384
tests/auto/qtqmlpromise/requirements/tst_requirements.qml
Normal file
384
tests/auto/qtqmlpromise/requirements/tst_requirements.qml
Normal file
@ -0,0 +1,384 @@
|
|||||||
|
import QtQuick 2.3
|
||||||
|
import QtPromise 1.0
|
||||||
|
import QtTest 1.0
|
||||||
|
|
||||||
|
// https://promisesaplus.com/#requirements
|
||||||
|
TestCase {
|
||||||
|
name: "Requirements"
|
||||||
|
|
||||||
|
// 2.1. Promise States
|
||||||
|
|
||||||
|
// 2.1.1 When pending, a promise:
|
||||||
|
function test_pendingFulfilled() {
|
||||||
|
// 2.1.1.1. may transition to either the fulfilled state
|
||||||
|
var p = new Promise(function(resolve) {
|
||||||
|
Qt.callLater(function() {
|
||||||
|
resolve(42);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
compare(p.isPending(), true);
|
||||||
|
compare(p.isFulfilled(), false);
|
||||||
|
compare(p.isRejected(), false);
|
||||||
|
|
||||||
|
p.wait();
|
||||||
|
|
||||||
|
compare(p.isPending(), false);
|
||||||
|
compare(p.isFulfilled(), true);
|
||||||
|
compare(p.isRejected(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2.1.1 When pending, a promise:
|
||||||
|
function test_pendingRejected() {
|
||||||
|
// 2.1.1.1. ... or the rejected state
|
||||||
|
var p = new Promise(function(resolve, reject) {
|
||||||
|
Qt.callLater(function() {
|
||||||
|
reject(new Error(42));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
compare(p.isPending(), true);
|
||||||
|
compare(p.isFulfilled(), false);
|
||||||
|
compare(p.isRejected(), false);
|
||||||
|
|
||||||
|
p.wait();
|
||||||
|
|
||||||
|
compare(p.isPending(), false);
|
||||||
|
compare(p.isFulfilled(), false);
|
||||||
|
compare(p.isRejected(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2.1.2. When fulfilled, a promise:
|
||||||
|
function test_fulfilled() {
|
||||||
|
var p = new Promise(function(resolve, reject) {
|
||||||
|
Qt.callLater(function() {
|
||||||
|
// 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(new Error("foo"));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
compare(p.isPending(), true);
|
||||||
|
|
||||||
|
var value = -1;
|
||||||
|
var error = null;
|
||||||
|
|
||||||
|
p.then(function(res) {
|
||||||
|
value = res;
|
||||||
|
}, function(err) {
|
||||||
|
error = err;
|
||||||
|
}).wait();
|
||||||
|
|
||||||
|
compare(p.isFulfilled(), true);
|
||||||
|
compare(p.isRejected(), false);
|
||||||
|
compare(error, null);
|
||||||
|
compare(value, 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2.1.3 When rejected, a promise:
|
||||||
|
function test_rejected() {
|
||||||
|
var p = new Promise(function(resolve, reject) {
|
||||||
|
Qt.callLater(function() {
|
||||||
|
// 2.1.3.2. must have a reason, which must not change.
|
||||||
|
reject(new Error("foo"));
|
||||||
|
reject(new Error("bar"));
|
||||||
|
|
||||||
|
// 2.1.3.1. must not transition to any other state.
|
||||||
|
resolve(42);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
compare(p.isPending(), true);
|
||||||
|
|
||||||
|
var value = -1;
|
||||||
|
var error = null;
|
||||||
|
|
||||||
|
p.then(function(res) {
|
||||||
|
value = res;
|
||||||
|
}, function(err) {
|
||||||
|
error = err;
|
||||||
|
}).wait();
|
||||||
|
|
||||||
|
compare(p.isFulfilled(), false);
|
||||||
|
compare(p.isRejected(), true);
|
||||||
|
compare(error instanceof Error, true);
|
||||||
|
compare(error.message, 'foo');
|
||||||
|
compare(value, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2.2. The then Method
|
||||||
|
|
||||||
|
// 2.2.1. Both onFulfilled and onRejected are given (resolve)
|
||||||
|
function test_thenArgsBothResolve() {
|
||||||
|
var error = null;
|
||||||
|
var value = -1;
|
||||||
|
|
||||||
|
Promise.resolve(42).then(
|
||||||
|
function(res) { value = res; },
|
||||||
|
function(err) { error = err; }
|
||||||
|
).wait();
|
||||||
|
|
||||||
|
compare(error, null);
|
||||||
|
compare(value, 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2.2.1. Both onFulfilled and onRejected are given (reject)
|
||||||
|
function test_thenArgsBothReject() {
|
||||||
|
var error = null;
|
||||||
|
var value = -1;
|
||||||
|
|
||||||
|
Promise.reject(new Error('foo')).then(
|
||||||
|
function(res) { value = res; },
|
||||||
|
function(err) { error = err; }
|
||||||
|
).wait();
|
||||||
|
|
||||||
|
compare(error instanceof Error, true);
|
||||||
|
compare(error.message, 'foo');
|
||||||
|
compare(value, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2.2.1. onFulfilled is an optional arguments:
|
||||||
|
function test_thenArgsSecond() {
|
||||||
|
var error = null;
|
||||||
|
|
||||||
|
Promise.reject(new Error('foo')).then(
|
||||||
|
null,
|
||||||
|
function(err) { error = err; }
|
||||||
|
).wait();
|
||||||
|
|
||||||
|
compare(error instanceof Error, true);
|
||||||
|
compare(error.message, 'foo');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2.2.1. onRejected is an optional arguments:
|
||||||
|
function test_thenArgsFirst() {
|
||||||
|
var value = -1;
|
||||||
|
|
||||||
|
Promise.resolve(42).then(
|
||||||
|
function(res) { value = res; }
|
||||||
|
).wait();
|
||||||
|
|
||||||
|
compare(value, 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2.2.1.1. If onFulfilled is not a function, it must be ignored.
|
||||||
|
function test_thenArgsFirstInvalid() {
|
||||||
|
var value = -1;
|
||||||
|
|
||||||
|
Promise.resolve(42).then('invalid').then(
|
||||||
|
function(res) { value = res; }
|
||||||
|
).wait();
|
||||||
|
|
||||||
|
compare(value, 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2.2.1.2. If onRejected is not a function, it must be ignored.
|
||||||
|
function test_thenArgsSecondInvalid() {
|
||||||
|
var error = -1;
|
||||||
|
|
||||||
|
Promise.reject(new Error('foo')).then(
|
||||||
|
null,
|
||||||
|
'invalid'
|
||||||
|
).then(
|
||||||
|
null,
|
||||||
|
function(err) { error = err; }
|
||||||
|
).wait();
|
||||||
|
|
||||||
|
compare(error instanceof Error, true);
|
||||||
|
compare(error.message, 'foo');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2.2.2. If onFulfilled is a function:
|
||||||
|
function test_thenOnFulfilled() {
|
||||||
|
var p0 = new Promise(function(resolve) {
|
||||||
|
Qt.callLater(function() {
|
||||||
|
// 2.2.2.3. it must not be called more than once
|
||||||
|
resolve(42);
|
||||||
|
resolve(43);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
var values = [];
|
||||||
|
var p1 = p0.then(function(res) {
|
||||||
|
values.push(res);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 2.2.2.2. it must not be called before promise is fulfilled.
|
||||||
|
compare(p0.isPending(), true);
|
||||||
|
compare(p1.isPending(), true);
|
||||||
|
compare(values.length, 0);
|
||||||
|
|
||||||
|
p1.wait();
|
||||||
|
|
||||||
|
// 2.2.2.1. it must be called after promise is fulfilled,
|
||||||
|
// with promise’s value as its first argument.
|
||||||
|
compare(p0.isFulfilled(), true);
|
||||||
|
compare(p1.isFulfilled(), true);
|
||||||
|
compare(values, [42]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2.2.3. If onRejected is a function:
|
||||||
|
function test_thenOnRejected() {
|
||||||
|
var p0 = new Promise(function(resolve, reject) {
|
||||||
|
Qt.callLater(function() {
|
||||||
|
// 2.2.2.3. it must not be called more than once
|
||||||
|
reject(new Error('foo'));
|
||||||
|
reject(new Error('bar'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
var errors = [];
|
||||||
|
var p1 = p0.then(null, function(res) {
|
||||||
|
errors.push(res);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 2.2.3.2. it must not be called before promise is rejected.
|
||||||
|
compare(p0.isPending(), true);
|
||||||
|
compare(p1.isPending(), true);
|
||||||
|
compare(errors.length, 0);
|
||||||
|
|
||||||
|
p1.wait();
|
||||||
|
|
||||||
|
// 2.2.3.1. it must be called after promise is rejected,
|
||||||
|
// with promise’s reason as its first argument.
|
||||||
|
compare(p0.isRejected(), true);
|
||||||
|
compare(p1.isFulfilled(), true);
|
||||||
|
compare(errors.length, 1);
|
||||||
|
compare(errors[0] instanceof Error, true);
|
||||||
|
compare(errors[0].message, 'foo');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2.2.4. onFulfilled or onRejected must not be called until the execution context
|
||||||
|
// stack contains only platform code (ie. executed asynchronously, after the event
|
||||||
|
// loop turn in which then is called, and with a fresh stack).
|
||||||
|
function test_thenAsynchronous()
|
||||||
|
{
|
||||||
|
var value = -1;
|
||||||
|
var p0 = Promise.resolve(42);
|
||||||
|
var p1 = p0.then(function(res){ value = res; });
|
||||||
|
|
||||||
|
compare(p0.isFulfilled(), true);
|
||||||
|
compare(p1.isPending(), true);
|
||||||
|
compare(value, -1);
|
||||||
|
|
||||||
|
p1.wait();
|
||||||
|
|
||||||
|
compare(p1.isFulfilled(), true);
|
||||||
|
compare(value, 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2.2.5 onFulfilled and onRejected must be called as functions (i.e. with no this value).
|
||||||
|
function test_thenThisArg() {
|
||||||
|
var scopes = [];
|
||||||
|
|
||||||
|
Promise.resolve(42).then(function() { scopes.push(this); }).wait();
|
||||||
|
Promise.reject(new Error('foo')).then(null, function() { scopes.push(this); }).wait();
|
||||||
|
|
||||||
|
// Qt doesn't allow to call JS function with undefined "this"
|
||||||
|
// Let's adopt the sloppy mode (this === the global object).
|
||||||
|
var global = (function() { return this; })();
|
||||||
|
compare(scopes, [global, global]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2.2.6. then may be called multiple times on the same promise:
|
||||||
|
|
||||||
|
// 2.2.6.1. If/when promise is fulfilled, all respective onFulfilled callbacks
|
||||||
|
// must execute in the order of their originating calls to then:
|
||||||
|
function test_thenMultipleCalls() {
|
||||||
|
var values = [];
|
||||||
|
var p = new Promise(function(resolve) {
|
||||||
|
Qt.callLater(function() {
|
||||||
|
resolve(42);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
Promise.all([
|
||||||
|
p.then(function(res) { return res + 1; }),
|
||||||
|
p.then(function(res) { return res + 2; }),
|
||||||
|
p.then(function(res) { return res + 3; })
|
||||||
|
]).then(function(res) {
|
||||||
|
values = res;
|
||||||
|
}).wait();
|
||||||
|
|
||||||
|
compare(values, [43, 44, 45]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
void tst_requirements::thenMultipleCalls()
|
||||||
|
{
|
||||||
|
// 2.2.6.2. If/when promise is rejected, all respective onRejected callbacks
|
||||||
|
// must execute in the order of their originating calls to then:
|
||||||
|
{
|
||||||
|
QVector<int> values;
|
||||||
|
QPromise<int> p([](const QPromiseResolve<int>&, const QPromiseReject<int>& reject) {
|
||||||
|
qtpromise_defer([=]() {
|
||||||
|
reject(8);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
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}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_requirements::thenHandlers()
|
||||||
|
{
|
||||||
|
// 2.2.7. then must return a promise: p2 = p1.then(onFulfilled, onRejected);
|
||||||
|
{
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2.2.7.1. If either onFulfilled or onRejected returns a value x, run the
|
||||||
|
// Promise Resolution Procedure [[Resolve]](p2, x) -> See 2.3.
|
||||||
|
|
||||||
|
// 2.2.7.2. If either onFulfilled or onRejected throws an exception e,
|
||||||
|
// p2 must be rejected with e as the reason.
|
||||||
|
{
|
||||||
|
QString reason;
|
||||||
|
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;
|
||||||
|
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"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2.2.7.3. If onFulfilled is not a function and promise1 is fulfilled,
|
||||||
|
// p2 must be fulfilled with the same value as promise1
|
||||||
|
{
|
||||||
|
QString value;
|
||||||
|
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([&](const QString& e) { value = e; }).wait();
|
||||||
|
|
||||||
|
QVERIFY(p1.isFulfilled());
|
||||||
|
QVERIFY(p2.isFulfilled());
|
||||||
|
QCOMPARE(value, QString("42"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
Loading…
Reference in New Issue
Block a user