QIfPendingReply Class

template <typename T> class QIfPendingReply

Template class for providing asynchronous results. More...

Header: #include <QIfPendingReply>
qmake: QT += interfaceframework
Instantiated By: PendingReply
Inherits: QIfPendingReplyBase

Public Functions

QIfPendingReply(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

QIfPendingReply<T> createFailedReply()
typename std::enable_if<QtPrivate::IsQEnumHelper<T>::Value, void>::type qIfRegisterPendingReplyType(const char *name = nullptr)
void qifRegisterPendingReplyBasicTypes()

Detailed Description

A QIfPendingReply 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, QIfPendingReply works also in QML and is especially made for this. The data stored in a QIfPendingReply 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 QIfPendingReply 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 qIfRegisterPendingReplyType function.

When a QIfPendingReply 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 QIfPendingReply has succeeded can be determined by the success property.

Writing a function returning a QIfPendingReply

When writing a function returning a QIfPendingReply, it is often needed to do some input validation and return before actual doing something. Without using a QIfPendingReply 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 QIfPendingReply the validation check needs to be fixed to return a valid QIfPendingReply. To make it more convenient to return a failed reply, the QIfPendingReply::createFailedReply() function be used.

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

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

    QIfPendingReply<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 QIfPendingReply 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 QIfPendingReply is marked as successful and the value set to the displayName().

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

Using functions returning a QIfPendingReply

When using a function which returns a QIfPendingReply, 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 QIfPendingReplyWatcher.

Signals and Slots

In order to keep the memory footprint low, the QIfPendingReply 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 QIfPendingReplyWatcher 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();
QIfPendingReply<QString> reply = displayName(uuid);
if (reply.isResultAvailable()) {
    if (reply.isSuccessfull())
        useDisplayName(reply.value());
    else
        qWarning("getting the displayName failed");
} else {
    connect(reply.watcher(), &QIfPendingReplyWatcher::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 QIfPendingReplyWatcher returned is owned by the QIfPendingReply and all its copies. If all copies of the QIfPendingReply get deleted its QIfPendingReplyWatcher gets deleted as well.

For usage in QML see the QML documentation.

Member Function Documentation

QIfPendingReply::QIfPendingReply(const T &value)

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

This is equivalent to:

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

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

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

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

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

T QIfPendingReply::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 QIfPendingReply::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 QIfPendingReply::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 QIfPendingReplyBase::then.

Related Non-Members

template <typename T> typename std::enable_if<QtPrivate::IsQEnumHelper<T>::Value, void>::type qIfRegisterPendingReplyType(const char *name = nullptr)

Registers the type name name for the type T for usage inside a QIfPendingReply. 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 qifRegisterPendingReplyBasicTypes()

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

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

© 2021 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.