Search code examples
c++qtqt4signals-slots

Wait for a SLOT to finish


I use QNetworkAccessManager to do form POST.

I have connected signals and slots as:

connect(manager,SIGNAL(finished(QNetworkReply*)),this,SLOT(readCookies(QNetworkReply*)));

Now, I make a request by doing:

manager->post(request,postData);

Now readCookies(QNetworkReply *) will be run as soon as SIGNAL is emitted. Now, using the Cookies which I get in this slot, I have to make one more POST..

As signals & slots are asynchronous, I want to wait till I get the cookies from my first POST and then I again want to do another post using the cookies I got in first POST like

//Setting new request, headers etc...
manager->post(request2,postData2);

I want the later to always be executed after first one has executed (so that I get proper cookies value).

What is the way to go? I am new to all these SIGNALS & SLOTS so please bear with me.


Solution

  • You can do the post in your readCookies() slot:

    void readCookies( QNetworkReply* reply ) {
        if ( ...error? ) {
            report error...
            return;
        }
    
        ...
        manager->post(request2,postData2);
    }
    

    I will be called when the cookies is read, and you can then continue with your post. Connect that to a second slot, and so on. Managing multiple, possibly parallely running asynchronous operations like this can become errorprone though, if you manage many of them in a single object. I would suggest to use the Command Pattern - here I described why I find it extremely useful in exactly this context. The sequence of request and asnychronous operations is encapsulated in a single object (abbreviated, with some pseudo-code):

    class PostStuffOperation : public QObject {
        Q_OBJECT
    public:
        enum Error {
           NoError=0,
           Error=1,
           ...
        };
    
        Error error() const; //operation successful or not?
        QString errorString() const; //human-readable error description
    
        ... setters for all the information the operation needs
        ...
        void start() {
           ...start your first request and connect it to cookiesRead 
        }
    
    public Q_SLOTS:
        void cookiesRead( QNetworkReply * ) {
        if ( error ) {
           // set error and errorString...
           emit finished( this ); //couldn't read cookies, so the operation fails
           return;
        }
        ... do post
     }
    
     void postFinished( QNetworkReply* ) {
         if ( error ) {
             // set error and errorString...
         }
    
        emit finished( this ); //post finished - that means the whole operation finished
     }
    Q_SIGNALS:
        void finished( PostStuffOperation* );
    };
    

    To start the operation, you do

    PostStuffOperation op* = new PostStuffOperation( this );
    ... pass data like server, port etc. to the operation
    connect( op, SIGNAL(finished()), this, SLOT(postOperationFinished()) );
    op->start();
    
    void postOperationFinished( PostStuffOperation* op ) {
        if ( op->error != PostStuffOperation::NoError ) {
            //handle error, e.g. show message box
        }
    }
    

    It makes sense to have a common baseclass for such operations, see e.g. KDE's KJob.