#include "promise_examples.h" #include "promise.h" #include #include #include namespace LtsUtils { // First Example void PromiseExample1_Class::doSomethingAsync1() { QTimer::singleShot(200, this, [this]() { emit signalDone1(true); }); } void PromiseExample1_Class::doSomethingAsync2() { QTimer::singleShot(450, this, [this]() { emit signalDone2(true, 1234); }); } PromiseExample1::PromiseExample1(QObject *parent) : QObject(parent) , m_success(false) { /* We have to work with a class that works asynchronous and need many emits and we have to wait for some results to come in. * But we want the developer to use this function without rewriting everything that could be handled by a Promise that could be provided. */ auto func = [this](ResultFunction resolve /* call this function if the promise was successful */ , ResultFunction reject /* call this function if the promise was not successful */) { // we create the class (or you could use it from somewhere else) PromiseExample1_Class *c = new PromiseExample1_Class(this); // we connect to the done signal for the first action (before actually calling the function that can return this signal) connect(c, &PromiseExample1_Class::signalDone1, this, [this, c, resolve, reject](bool didThisWork) { // did the first action not work? if (!didThisWork) { // something went wrong and we have to exit here with a reject reject(QStringLiteral("First Action did not succeed")); // .. did not work.. at least tell the enduser what happened (the reject function has to print the string, though) <= promise is done, but failed early return; } else // yes, it worked! we can continue! { // after the first action succeeded, we can call the next action, that would otherwise be useless to call if the first call failed // we do the same thing as with the last signal connect here connect(c, &PromiseExample1_Class::signalDone2, this, [resolve, reject](bool didThisWork, int someNumberThatReturns) { if (didThisWork) // did the secondary action succeed? resolve(someNumberThatReturns); // .. then return the returned number to the resolve function <= this is the final point where the promise is done! else reject(QStringLiteral("Second Action did not succeed")); // .. did not work.. at least tell the enduser what happened (the reject function has to print the string, though) <= promise is done, but failed // everything done! }); c->doSomethingAsync2(); // now, finally, execute the second action - should call signalDone2 soon! } }); c->doSomethingAsync1(); // now, finally, execute this async stuff and then signalDone1 should be called soon! }; Promise *p = new Promise(func, this); // the promise was created, but will not execute until Qt is back into the event loop // and to receive resolve(..) or reject(..) messages, we have to connect to them first (but note that this works without connects) p->resolved([this](const QVariant &value) { // "someNumberThatReturns" is inside value qDebug() << "example1 promise succeeded! 'someNumberThatReturns' was" << value; m_success = true; }).rejected([](const QVariant &value) // <= you can directly call rejected(..) on resolved(..) to save time, as resolved(..) returns the promise itself, just like in js { // one or both of the actions didn't work - tell the user where it failed qDebug() << "example1 promise failed.." << value; }); } // End of First Example // Second Example PromiseExample2::PromiseExample2(QObject *parent) : QObject(parent) , m_success(false) { /* There is an asynchonous function inside the Promise that might not resolve because of some error. * A timeout is set for the Promise to reject so it doesn't get stuck. */ Promise *p = new Promise([](ResultFunction , ResultFunction ) { // never resolve for this test case }, this); p->setTimeout(1000); p->resolved([](const QVariant &) { QFAIL("This test case's promise should never resolve"); }); p->rejected([this](const QVariant &val) { QVERIFY2(val == QStringLiteral("timeout"), "Promise didn't reject with string \"timeout\""); qDebug() << "Promise rejected as expected"; m_success = true; }); } // End of Second Example // 3rd Example PromiseExample3::PromiseExample3(QObject *parent) : QObject(parent) , m_success(true) , m_alreadyRejected(false) { /* The promise's function failed with a reject, additionally a timeout was set * to reject a promise that might never resolve/reject. After the promise was rejected * the timeout should never trigger (because the promise was already rejected). */ Promise *p = new Promise([](ResultFunction , ResultFunction reject) { reject(QString()); // immediately reject }, this); p->setTimeout(1000); // set timeout p->resolved([](const QVariant &) { QFAIL("This test case's promise should never resolve"); }); p->rejected([this](const QVariant &val) { qDebug() << "rejected with val" << val; QVERIFY2(val == QString(), "Promise didn't reject with an expected empty string."); QVERIFY2(!m_alreadyRejected, "Promise rejected more than once"); m_alreadyRejected = true; qDebug() << "Promise rejected as expected"; }); } // End of 3rd Example } // namespace LtsUtils