Skip to content
#include <QCoroSignal>
target_link_libraries(myapp QCoro::Core)
QT += QCoroCore


It's possible to co_await a single signal emission through a special overload of the qCoro() function. The below function returns an awaitable that will suspend the current coroutine until the specified signal is emitted.

Task<SignalResult> qCoro(QObject *obj, QtSignalPtr ptr);

The arguments are a pointer to a QObject-derived object and a pointer to a the object's signal to connect to. Note that if the object is destroyed while being co_awaited, the coroutine will never be resumed.

The returned awaitable produces the signal's arguments. That is, if the signal has no arguments, the result of the awaitable will be void. If the signal has exactly one argument, then the awaitable produces the value of the argument. If the signal has more than one arguments, then the result of the awaitable is a std::tuple with all the arguments.


// void signal
co_await qCoro(timer, &QTimer::timeout);

// single-argument signal
const QUrl url = co_await qCoro(reply, &QNetworkReply::redirected);

// multi-argument signal, captured using structured bindings
const auto [exitCode, exitStatus] = co_await qCoro(process, &QProcess::finished);

Task<std::optional<SignalResult>> qCoro(QObject *obj, QtSignalPtr ptr, std::chrono::milliseconds timeout);

An overload that behaves similar to the two-argument overload, but takes an additional timeout argument. If the signal is not emitted within the specified timeout, the returned awaitable produces an empty std::optional. Otherwise the return type behaves the same way as the two-argument overload.


A helper function that creates an AsyncGenerator which yields a value whenever the signal is emitted.

QCoro::AsyncGenerator<SignalArgs> qCoroSignalListener(QObject *obj, QtSignalPtr ptr, std::chrono::milliseconds timeout);

The function takes up to three arguments, the obj and ptr are a QObject-derived object and a pointer to a signal member function to connect to. The third timeout argument is optional. When the timeout is set, the generator will end if the signal is not emitted within the specified timeout. When not set, or set to -1, the generator will never terminate on its own, even if the obj QObject is destroyed!

The generator produces all signal emissions, even those that ocur in between the generator being co_awaited. In the example below, even when the QNetworkReply::downloadProgress() signal is emitted while asynchronously processing something in the middle of the while loop body, the emission will not be lost. It will be enqueued, and returned synchronously with the next co_await ++it call.

auto listener = qCoroSignalListener(networkReply, &QNetworkReply::downloadProgress);
auto it = co_await listener.begin(); // waits for first emission
while (it != listener.end() && networkReply->isRunning()) {
    const auto [received, total] = *it;
    // do something with results
    if (received == total || networkReply->isFinished()) {
    co_await ++it; // waits for next signal emission

Alternatively, it's possible to use QCORO_FOREACH to look over the generator:

QCORO_FOREACH(const auto [received, total], qCoroSignalListener(reply, &QNetworkReply::downloadProgress)) {
    // do something with `received` and `total` values
    if (received == total || reply->isFinished()) {

Listener doesn't stop listening until destroyed.

Keep in mind that the listener generator will keep listening and collecting the signal emissions until it is destroyed, even if you are no longer actively iterating over the generator. It is recommended that you destroy the generator as soon as possible when you no longer need it.