Home > Net >  Qt creator make network requests without creating a callback hell
Qt creator make network requests without creating a callback hell

Time:01-14

So, I have this simplified code as an example for my problem:

#include <QDebug>
#include <functional>

void third()
{
    qInfo() << "finished everything!";
}

// will use stuff downloaded by `first()`, which will be used by `third`
void second(const std::function<void()>& cb = [] {})
{
    // this has to be called after `first()` has been called and finished

    // finished, calling callback
    cb();
}

// Will download some stuff, which will be used by `second()`
void first(const std::function<void()>& cb = [] {})
{
    // finished, calling callback
    cb();
}

int main(int argc, char* argv[])
{
    first([=] {
        second(third);
    });

    // or, even worse
    first([=] {
        second([=] {
            third();
        });
    });
}

Is there any way, that I can prevent this callback hell from happening? I thought about making another thread, where I would call those functions, block that thread and wait for them to finish.

I am not super sure on how I would do that though. So, first of all, is there a better way to write this (maybe even without creating another thread?) and secondly, if I had to make this happen on another thread, how would I do that?

Thanks for any help in advance!

CodePudding user response:

Signals and slots quite naturally clean up this kind of code. For example:

class Downloader : public QObject {
   Q_OBJECT
public:
   void beginDownload() // this is your first
   {
      emit downloadFinished({});
   }
signals:
   void downloadFinished(const QByteArray& data);
};

class DataProcessorA: public QObject {
   Q_OBJECT
public:
   void processData(const QByteArray& data) // this is your second
   {
      emit processingFinished({});
   }
signals:
   void processingFinished(const QDateArray& processedData);
};

class DataProcessorB: public QObject {
   Q_OBJECT
public:
   void processData2(const QByteArray& data) // this is your third
   {
      emit processingFinished({});
   }
signals:
   void processingFinished(const QDateArray& processedData);
};

void myFunction()
{
   auto* downloader = new Downloader(parent);
   auto* processorA = new DataProcessorA(parent);
   auto* processorB = new DataProcessorB(parent);
   // make first call second...
   QObject::connect(downloader, &Downloader::downloadFinished, processorA, &DataProcessorA::processData);
   // make second call third...
   QObject::connect(processorA , &DataProcessorA::processingFinished, processorB, &DataProcessorB::processData);
}

This can work even if the download is executed on a separate thread because Qt's signals/slots can work between threads. The data in the signal will be copied to the receiving thread. Ref: https://doc.qt.io/qt-6/qt.html#ConnectionType-enum

There is a bit of boilerplate with all the QObject stuff, but this should allow you to write each piece in isolation and then be able to flexibly connect the individual pieces using QObject::connect.

  • Related