From 69c07855f476a930345d0a5840ea3dfadc296c07 Mon Sep 17 00:00:00 2001 From: Simon Brunel Date: Sat, 26 May 2018 11:40:51 +0200 Subject: [PATCH] Implement QPromise>::map(mapper) (#15) Iterate over all the promise value (i.e. `Sequence`) and map the sequence to another using the given `mapper` function. Also provide a static helper to directly map values (`QtPromise::map(values, mapper)`). --- docs/SUMMARY.md | 2 + docs/qtpromise/api-reference.md | 2 + docs/qtpromise/helpers/map.md | 43 +++++ docs/qtpromise/qpromise/map.md | 57 +++++++ src/qtpromise/qpromise.h | 4 + src/qtpromise/qpromise.inl | 11 ++ src/qtpromise/qpromise_p.h | 12 ++ src/qtpromise/qpromisehelpers.h | 25 +++ tests/auto/qtpromise/helpers/helpers.pro | 1 + tests/auto/qtpromise/helpers/map/map.pro | 4 + tests/auto/qtpromise/helpers/map/tst_map.cpp | 151 +++++++++++++++++ tests/auto/qtpromise/qpromise/map/map.pro | 4 + tests/auto/qtpromise/qpromise/map/tst_map.cpp | 157 ++++++++++++++++++ tests/auto/qtpromise/qpromise/qpromise.pro | 1 + 14 files changed, 474 insertions(+) create mode 100644 docs/qtpromise/helpers/map.md create mode 100644 docs/qtpromise/qpromise/map.md create mode 100644 tests/auto/qtpromise/helpers/map/map.pro create mode 100644 tests/auto/qtpromise/helpers/map/tst_map.cpp create mode 100644 tests/auto/qtpromise/qpromise/map/map.pro create mode 100644 tests/auto/qtpromise/qpromise/map/tst_map.cpp diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index 8e6072e..b472a19 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -10,6 +10,7 @@ * [.isFulfilled](qtpromise/qpromise/isfulfilled.md) * [.isPending](qtpromise/qpromise/ispending.md) * [.isRejected](qtpromise/qpromise/isrejected.md) + * [.map](qtpromise/qpromise/map.md) * [.tap](qtpromise/qpromise/tap.md) * [.tapFail](qtpromise/qpromise/tapfail.md) * [.then](qtpromise/qpromise/then.md) @@ -20,3 +21,4 @@ * [::resolve (static)](qtpromise/qpromise/resolve.md) * [qPromise](qtpromise/helpers/qpromise.md) * [qPromiseAll](qtpromise/helpers/qpromiseall.md) + * [QtPromise::map](qtpromise/helpers/map.md) diff --git a/docs/qtpromise/api-reference.md b/docs/qtpromise/api-reference.md index c464f04..483b305 100644 --- a/docs/qtpromise/api-reference.md +++ b/docs/qtpromise/api-reference.md @@ -9,6 +9,7 @@ * [`QPromise::isFulfilled`](qpromise/isfulfilled.md) * [`QPromise::isPending`](qpromise/ispending.md) * [`QPromise::isRejected`](qpromise/isrejected.md) +* [`QPromise::map`](qpromise/map.md) * [`QPromise::tap`](qpromise/tap.md) * [`QPromise::tapFail`](qpromise/tapfail.md) * [`QPromise::then`](qpromise/then.md) @@ -25,3 +26,4 @@ * [`qPromise`](helpers/qpromise.md) * [`qPromiseAll`](helpers/qpromiseall.md) +* [`QtPromise::map`](helpers/map.md) diff --git a/docs/qtpromise/helpers/map.md b/docs/qtpromise/helpers/map.md new file mode 100644 index 0000000..d7ab5a1 --- /dev/null +++ b/docs/qtpromise/helpers/map.md @@ -0,0 +1,43 @@ +## `QtPromise::map` + +```cpp +QtPromise::map(Sequence values, Mapper mapper) -> QPromise> + +// With: +// - Sequence: STL compatible container (e.g. QVector, etc.) +// - Mapper: Function(T value, int index) -> R | QPromise +``` + +Iterates over `values` and [maps the sequence](https://en.wikipedia.org/wiki/Map_%28higher-order_function%29) +to another using the given `mapper` function. The type returned by `mapper` determines the type +of the `output` promise. If `mapper` throws, `output` is rejected with the new exception. + +If `mapper` returns a promise (or `QFuture`), the `output` promise is delayed until all the +promises are resolved. If any of the promises fails, `output` immediately rejects with the +error of the promise that rejected, whether or not the other promises are resolved. + +```cpp +auto output = QtPromise::map(QVector{ + QUrl("http://a..."), + QUrl("http://b..."), + QUrl("http://c...") +}, [](const QUrl& url, ...) { + return QPromise([&](auto resolve, auto reject) { + // download content at url and resolve + // {...} + }); +}); + +// 'output' resolves as soon as all promises returned by +// 'mapper' are fulfilled or at least one is rejected. + +// 'output' type: QPromise> +output.then([](const QVector& res) { + // {...} +}); +``` + +> **Note:** the order of the output sequence values is guarantee to be the same as the original +sequence, regardless of completion order of the promises returned by `mapper`. + +See also: [`QPromise::map`](../qpromise/map.md) diff --git a/docs/qtpromise/qpromise/map.md b/docs/qtpromise/qpromise/map.md new file mode 100644 index 0000000..531dee7 --- /dev/null +++ b/docs/qtpromise/qpromise/map.md @@ -0,0 +1,57 @@ +## `QPromise>::map` + +> **Important:** applies only to promise with sequence value. + +```cpp +QPromise>::map(Mapper mapper) -> QPromise> + +// With: +// - Sequence: STL compatible container (e.g. QVector, etc.) +// - Mapper: Function(T value, int index) -> R | QPromise +``` + +Iterates over all the promise values (i.e. `Sequence`) and [maps the sequence](https://en.wikipedia.org/wiki/Map_%28higher-order_function%29) +to another using the given `mapper` function. The type returned by `mapper` determines the type +of the `output` promise. If `mapper` throws, `output` is rejected with the new exception. + +If `mapper` returns a promise (or `QFuture`), the `output` promise is delayed until all the +promises are resolved. If any of the promises fails, `output` immediately rejects with the +error of the promise that rejected, whether or not the other promises are resolved. + +```cpp +QPromise> input = {...} + +auto output = input.map([](const QUrl& url, int index) { + return QPromise([&](auto resolve, auto reject) { + // download content at 'url' and resolve + // {...} + }); +}).map([](const QByteArray& value, ...) { + // process the downloaded QByteArray + // {...} + return DownloadResult(value); +}); + +// 'output' resolves as soon as all promises returned by +// 'mapper' are fulfilled or at least one is rejected. + +// 'output' type: QPromise> +output.then([](const QVector& res) { + // {...} +}); +``` + +> **Note:** the order of the output sequence values is guarantee to be the same as the original +sequence, regardless of completion order of the promises returned by `mapper`. + +This function is provided for convenience and is similar to: + +```cpp +promise.then([](const Sequence& values) { + return QtPromise::map(values, [](const T& value, int index) { + return // {...} + }); +}); +``` + +See also: [`QtPromise::map`](../helpers/map.md) diff --git a/src/qtpromise/qpromise.h b/src/qtpromise/qpromise.h index 7270e12..5f9272d 100644 --- a/src/qtpromise/qpromise.h +++ b/src/qtpromise/qpromise.h @@ -88,6 +88,10 @@ public: template QPromise(F&& resolver): QPromiseBase(std::forward(resolver)) { } + template + inline typename QtPromisePrivate::PromiseMapper::PromiseType + map(Functor fn); + public: // STATIC template