diff --git a/README.md b/README.md
index 9d08b3a..174fcbf 100644
--- a/README.md
+++ b/README.md
@@ -300,6 +300,19 @@ auto output = input.finally([]() {
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::tap(handler) -> QPromise`
+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
+auto output = input.tap([](int res) {
+ log(res);
+}).then([](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::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 ab6e28b..dd9ca39 100644
--- a/src/qtpromise/qpromise.h
+++ b/src/qtpromise/qpromise.h
@@ -43,6 +43,9 @@ public:
template
inline QPromise finally(THandler handler) const;
+ template
+ inline QPromise tap(THandler handler) 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 2f99be0..ee28700 100644
--- a/src/qtpromise/qpromise.inl
+++ b/src/qtpromise/qpromise.inl
@@ -139,6 +139,16 @@ inline QPromise QPromiseBase::finally(THandler handler) const
});
}
+template
+template
+inline QPromise QPromiseBase::tap(THandler handler) const
+{
+ QPromise p = *this;
+ return p.then(handler).then([=]() {
+ return p;
+ });
+}
+
template
inline QPromise QPromiseBase::wait() const
{
diff --git a/tests/auto/benchmark/tst_benchmark.cpp b/tests/auto/benchmark/tst_benchmark.cpp
index 885d1f9..0653a23 100644
--- a/tests/auto/benchmark/tst_benchmark.cpp
+++ b/tests/auto/benchmark/tst_benchmark.cpp
@@ -15,6 +15,7 @@ private Q_SLOTS:
void valueReject();
void valueThen();
void valueFinally();
+ void valueTap();
void valueDelayed();
void errorReject();
void errorThen();
@@ -224,6 +225,36 @@ void tst_benchmark::valueFinally()
}
}
+void tst_benchmark::valueTap()
+{
+ { // should not copy the value on continutation if fulfilled
+ int value = -1;
+ Data::logs().reset();
+ QPromise::resolve(Data(42)).tap([&](const Data& res) {
+ value = res.value();
+ }).wait();
+
+ QCOMPARE(Data::logs().ctor, 1);
+ QCOMPARE(Data::logs().copy, 0);
+ QCOMPARE(Data::logs().move, 1); // move value to the input and output promise data
+ QCOMPARE(Data::logs().refs, 0);
+ QCOMPARE(value, 42);
+ }
+ { // should not create value on continutation if rejected
+ int value = -1;
+ Data::logs().reset();
+ QPromise::reject(QString("foo")).tap([&](const Data& res) {
+ value = res.value();
+ }).wait();
+
+ QCOMPARE(Data::logs().ctor, 0);
+ QCOMPARE(Data::logs().copy, 0);
+ QCOMPARE(Data::logs().move, 0);
+ QCOMPARE(Data::logs().refs, 0);
+ QCOMPARE(value, -1);
+ }
+}
+
void tst_benchmark::errorReject()
{
{ // should create one copy of the error when rejected by rvalue
diff --git a/tests/auto/qpromise/tst_qpromise.cpp b/tests/auto/qpromise/tst_qpromise.cpp
index 14874d9..2b41528 100644
--- a/tests/auto/qpromise/tst_qpromise.cpp
+++ b/tests/auto/qpromise/tst_qpromise.cpp
@@ -39,6 +39,15 @@ private Q_SLOTS:
void finallyDelayedResolved();
void finallyDelayedRejected();
+ void tapFulfilled();
+ void tapFulfilled_void();
+ void tapRejected();
+ void tapRejected_void();
+ void tapThrows();
+ void tapThrows_void();
+ void tapDelayedResolved();
+ void tapDelayedRejected();
+
}; // class tst_qpromise
QTEST_MAIN(tst_qpromise)
@@ -528,3 +537,120 @@ void tst_qpromise::finallyDelayedRejected()
QCOMPARE(p.isRejected(), true);
}
}
+
+void tst_qpromise::tapFulfilled()
+{
+ int value = -1;
+ auto p = QPromise::resolve(42).tap([&](int res) {
+ value = res + 1;
+ return 8;
+ });
+
+ QCOMPARE(waitForValue(p, 42), 42);
+ QCOMPARE(p.isFulfilled(), true);
+ QCOMPARE(value, 43);
+}
+
+void tst_qpromise::tapFulfilled_void()
+{
+ int value = -1;
+ auto p = QPromise::resolve().tap([&]() {
+ value = 43;
+ return 8;
+ });
+
+ QCOMPARE(waitForValue(p, -1, 42), 42);
+ QCOMPARE(p.isFulfilled(), true);
+ QCOMPARE(value, 43);
+}
+
+void tst_qpromise::tapRejected()
+{
+ int value = -1;
+ auto p = QPromise::reject(QString("foo")).tap([&](int res) {
+ value = res + 1;
+ });
+
+ QCOMPARE(waitForError(p, QString()), QString("foo"));
+ QCOMPARE(p.isRejected(), true);
+ QCOMPARE(value, -1);
+}
+
+void tst_qpromise::tapRejected_void()
+{
+ int value = -1;
+ auto p = QPromise::reject(QString("foo")).tap([&]() {
+ value = 43;
+ });
+
+ QCOMPARE(waitForError(p, QString()), QString("foo"));
+ QCOMPARE(p.isRejected(), true);
+ QCOMPARE(value, -1);
+}
+
+void tst_qpromise::tapThrows()
+{
+ auto p = QPromise::resolve(42).tap([&](int) {
+ throw QString("foo");
+ });
+
+ QCOMPARE(waitForError(p, QString()), QString("foo"));
+ QCOMPARE(p.isRejected(), true);
+}
+
+void tst_qpromise::tapThrows_void()
+{
+ auto p = QPromise::resolve().tap([&]() {
+ throw QString("foo");
+ });
+
+ QCOMPARE(waitForError(p, QString()), QString("foo"));
+ QCOMPARE(p.isRejected(), true);
+}
+
+void tst_qpromise::tapDelayedResolved()
+{
+ QVector values;
+ auto p = QPromise::resolve(1).tap([&](int) {
+ QPromise p([&](const QPromiseResolve& resolve) {
+ qtpromise_defer([=, &values]() {
+ values << 3;
+ resolve(4); // ignored!
+ });
+ });
+
+ values << 2;
+ return p;
+ });
+
+ p.then([&](int r) {
+ values << r;
+ }).wait();
+
+ QCOMPARE(p.isFulfilled(), true);
+ QCOMPARE(values, QVector({2, 3, 1}));
+}
+
+void tst_qpromise::tapDelayedRejected()
+{
+ QVector values;
+ auto p = QPromise::resolve(1).tap([&](int) {
+ QPromise p([&](const QPromiseResolve&, const QPromiseReject& reject) {
+ qtpromise_defer([=, &values]() {
+ values << 3;
+ reject(QString("foo"));
+ });
+ });
+
+ values << 2;
+ return p;
+ });
+
+ p.then([&](int r) {
+ values << r;
+ }).wait();
+
+ QCOMPARE(waitForError(p, QString()), QString("foo"));
+ QCOMPARE(p.isRejected(), true);
+ QCOMPARE(values, QVector({2, 3}));
+}