diff --git a/README.md b/README.md index 174fcbf..935e4c9 100644 --- a/README.md +++ b/README.md @@ -304,6 +304,7 @@ If `handler` returns a promise (or QFuture), the `output` promise is delayed unt This `handler` allows to observe the value of the `input` promise, without changing the propagated value. The `output` promise will be resolved with the same value as the `input` promise (the `handler` returned value will be ignored). However, if `handler` throws, `output` is rejected with the new exception. Unlike [`finally`](#qpromise-finally), this handler is **not** called for rejections. ```cpp +QPromise input = {...} auto output = input.tap([](int res) { log(res); }).then([](int res) { @@ -313,6 +314,16 @@ auto output = input.tap([](int res) { If `handler` returns a promise (or QFuture), the `output` promise is delayed until the returned promise is resolved and under the same conditions: the delayed value is ignored, the error transmitted to the `output` promise. +### `QPromise::delay(handler) -> QPromise` +This method returns a promise that will be fulfilled with the same value as the `input` promise and after at least `msec` milliseconds. If the `input` promise is rejected, the `output` promise is immediately rejected with the same reason. + +```cpp +QPromise input = {...} +auto output = input.delay(2000).then([](int res) { + // called 2 seconds after `input` is fulfilled +}); +``` + ### `QPromise::wait() -> QPromise` This method holds the execution of the remaining code **without** blocking the event loop of the current thread: diff --git a/src/qtpromise/qpromise.h b/src/qtpromise/qpromise.h index dd9ca39..e83d23a 100644 --- a/src/qtpromise/qpromise.h +++ b/src/qtpromise/qpromise.h @@ -46,6 +46,7 @@ public: template inline QPromise tap(THandler handler) const; + inline QPromise delay(int msec) const; inline QPromise wait() const; void swap(QPromiseBase& other) { qSwap(m_d, other.m_d); } diff --git a/src/qtpromise/qpromise.inl b/src/qtpromise/qpromise.inl index ee28700..2778254 100644 --- a/src/qtpromise/qpromise.inl +++ b/src/qtpromise/qpromise.inl @@ -1,6 +1,7 @@ // Qt #include #include +#include namespace QtPromise { @@ -149,6 +150,16 @@ inline QPromise QPromiseBase::tap(THandler handler) const }); } +template +inline QPromise QPromiseBase::delay(int msec) const +{ + return tap([=]() { + return QPromise([&](const QPromiseResolve& resolve) { + QTimer::singleShot(msec, resolve); + }); + }); +} + template inline QPromise QPromiseBase::wait() const { diff --git a/tests/auto/qpromise/tst_qpromise.cpp b/tests/auto/qpromise/tst_qpromise.cpp index 2b41528..4dbfbee 100644 --- a/tests/auto/qpromise/tst_qpromise.cpp +++ b/tests/auto/qpromise/tst_qpromise.cpp @@ -3,6 +3,7 @@ // Qt #include +#include using namespace QtPromise; using namespace QtPromisePrivate; @@ -48,6 +49,9 @@ private Q_SLOTS: void tapDelayedResolved(); void tapDelayedRejected(); + void delayFulfilled(); + void delayRejected(); + }; // class tst_qpromise QTEST_MAIN(tst_qpromise) @@ -654,3 +658,36 @@ void tst_qpromise::tapDelayedRejected() QCOMPARE(p.isRejected(), true); QCOMPARE(values, QVector({2, 3})); } + +void tst_qpromise::delayFulfilled() +{ + QElapsedTimer timer; + qint64 elapsed = -1; + + timer.start(); + + auto p = QPromise::resolve(42).delay(1000).finally([&]() { + elapsed = timer.elapsed(); + }); + + QCOMPARE(waitForValue(p, -1), 42); + QCOMPARE(p.isFulfilled(), true); + QVERIFY(elapsed >= 1000 * 0.95); // Qt::CoarseTimer (default) Coarse timers try to + QVERIFY(elapsed <= 1000 * 1.05); // keep accuracy within 5% of the desired interval. +} + +void tst_qpromise::delayRejected() +{ + QElapsedTimer timer; + qint64 elapsed = -1; + + timer.start(); + + auto p = QPromise::reject(QString("foo")).delay(1000).finally([&]() { + elapsed = timer.elapsed(); + }); + + QCOMPARE(waitForError(p, QString()), QString("foo")); + QCOMPARE(p.isRejected(), true); + QVERIFY(elapsed < 5); +}