167 lines
5.4 KiB
C++
Executable File
167 lines
5.4 KiB
C++
Executable File
#include "promise_examples.h"
|
|
#include "promise.h"
|
|
|
|
#include <QTimer>
|
|
#include <QTest>
|
|
|
|
|
|
#include <QDebug>
|
|
|
|
|
|
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
|