QIviPendingReply Class

Template class for providing asynchronous results. More...

Header: #include <QIviPendingReply>
qmake: QT += ivicore
Instantiated By: PendingReply
Inherits: QIviPendingReplyBase

Public Functions

QIviPendingReply(const T &value)
T reply() const
void setSuccess(const T &val)
void then(const std::function<void (const T &)> &success, const std::function<void ()> &failed = std::function<void()>())

Static Public Members

QIviPendingReply<T> createFailedReply()
void qIviRegisterPendingReplyType(const char *name = nullptr)
void qiviRegisterPendingReplyBasicTypes()

Detailed Description

A QIviPendingReply is a template class for providing asynchronous results. It can be used as a return value for asynchronous functions, similar to QFuture.

In contrast to QFuture, QIviPendingReply works also in QML and is especially made for this. The data stored in a QIviPendingReply is implicitly shared between all copies of this reply object. This keeps the memory and performance footprint low.

The QML API is very similar to JavaScript Promises, at the same time the C++ API provides support for Qt's signals and slots.

The QIviPendingReply holds a result of a specific type. The type needs to have a default constructor and a copy constructor. By default the most Qt basic types are supported. New types can be added by using the qIviRegisterPendingReplyType function.

When a QIviPendingReply is created it does not have a valid result set yet. This can be checked by using the resultAvailable property. A result for a reply can be set by using the setFailed or setSuccess functions. Setting the result with this function can only be done once and cannot be changed later. Whether a QIviPendingReply has succeeded can be determined by the success property.

Writing a function returning a QIviPendingReply

When writing a function returning a QIviPendingReply, it is often needed to do some input validation and return before actual doing something. Without using a QIviPendingReply one would write a function as follows:

QString displayName(const QUuid &id)
{
    if (id.isNull)
        return QString();

    //do something and wait until the result is ready (synchronous)
    asyncAPI.getDisplayName(id);
    asyncAPI.waitForFinished(&displayNameChanged);
    return asyncAPI.displayName();
}

This function is using an asynchronous API e.g. provided by an IPC. getDisplayName(id) starts the task and once a result is ready the displayNameChanged signal is emitted and the actual value can be read using the displayName() function. The provided function is using a waitForFinished() method to actual wait for the signal to be emitted and return the value and make this API synchronous.

When moving this code to using QIviPendingReply the validation check needs to be fixed to return a valid QIviPendingReply. To make it more convenient to return a failed reply, the QIviPendingReply::createFailedReply() function be used.

Rewriting the above function to be fully asynchronous using a QIviPendingReply it would look like this:

QIviPendingReply<QString> displayName(const QUuid &id)
{
    if (id.isNull)
        return QIviPendingReply<QString>::createFailedReply();

    QIviPendingReply<QString> reply
    //connect to the change signal and set the result to the async reply when ready
    connect(asyncAPI, &displayNameChanged, this, [reply, asyncAPI]() mutable {
            reply.setSuccess(asyncAPI.displayName());
    });
    //start getting the name
    asyncAPI.getDisplayName(id);
    return reply;
}

Now a new QIviPendingReply is created right away and passed to the lamda used in the connect statement. The actual task is started afterwards and the reply object is returned. Once the async API emits the displayNameChanged signal the lamda is executed the QIviPendingReply is marked as successful and the value set to the displayName().

Note: All copies of a QIviPendingReply use implicit sharing. This data is freed once all copies of the pending replies are deleted.

Using functions returning a QIviPendingReply

When using a function which returns a QIviPendingReply, the first thing to do is to check whether a result is already available using the isResultAvailable property and act accordingly. Afterwards you can start to connect the signals provided by the QIviPendingReplyWatcher.

Signals and Slots

In order to keep the memory footprint low, the QIviPendingReply doesn't provide signals directly, as it doesn't need to derive from QObject, but uses the Q_GADGET macro instead. To get notified once a result is ready, the QIviPendingReplyWatcher can be used instead. The watcher can be retrieved using the watcher property.

Here an example on how this would work when using the API described above:

QUuid uuid = createUuid();
QIviPendingReply<QString> reply = displayName(uuid);
if (reply.isResultAvailable()) {
    if (reply.isSuccessfull())
        useDisplayName(reply.value());
    else
        qWarning("getting the displayName failed");
} else {
    connect(reply.watcher(), &QIviPendingReplyWatcher::valueChanged, this, [this, reply]() {
        if (reply.isSuccessfull())
            useDisplayName(reply.value());
        else
            qWarning("getting the displayName failed");
    });
}

As described above, the pending reply is checked first for whether a result is already available and if not, the signals from the watcher are used to react to the valueChanged signal.

Note: The QIviPendingReplyWatcher returned is owned by the QIviPendingReply and all its copies. If all copies of the QIviPendingReply get deleted its QIviPendingReplyWatcher gets deleted as well.

For usage in QML see the QML documentation.

Member Function Documentation

QIviPendingReply::QIviPendingReply(const T &value)

Creates a new QIviPendingReply that stores type T. The pending reply is set to successful using value.

This is equivalent to:

QIviPendingReply<T> reply.
reply.setSuccess(value);

[static] QIviPendingReply<T> QIviPendingReply::createFailedReply()

Creates a reply object which is marked as failed. This is convenient in error cases inside functions returning a reply e.g.

QIviPendingReply<QString> doSomething(int value)
{
    if (value <= 0) {
        qWarning("The value needs to be bigger than 0");
        return QIviPendingReply<QString>::createFailedReply()
    }

    QIviPendingReply<QString> reply;
    ...
    return reply;
}

T QIviPendingReply::reply() const

Returns the result of the reply. If no result has been set yet or when the reply is marked as failed, a default constructed value is returned.

See also setSuccess and setFailed.

void QIviPendingReply::setSuccess(const T &val)

Sets the result of the reply to val and marks the reply as succeeded.

Note: a result can only be set once and cannot be changed again later.

See also setFailed.

void QIviPendingReply::then(const std::function<void (const T &)> &success, const std::function<void ()> &failed = std::function<void()>())

Sets the C++ callbacks to be called once a result is delivered. If the reply succeeds success is called; otherwise failed is called.

The success callback gets the reply value as an argument.

In case the result of the pending reply is already available when this function is called, the corresponding callback functions are run immediately.

See also QIviPendingReplyBase::then.

Related Non-Members

void qIviRegisterPendingReplyType(const char *name = nullptr)

Registers the type name name for the type T for usage inside a QIviPendingReply. Any class or struct that has a public default constructor, a public copy constructor and a public destructor can be registered.

This function requires that T is a fully defined type at the point where the function is called. For pointer types, it also requires that the pointed-to type is fully defined. Use Q_DECLARE_OPAQUE_POINTER() to be able to register pointers to forward declared types.

Please see qRegisterMetaType for more information.

void qiviRegisterPendingReplyBasicTypes()

Registers QIviPendingReplys of all Qt basic types to the meta type system.

Usually this function called automatically when creating a QCoreApplication or a QIviPendingReply and doesn't need to be called manually.

© 2020 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.