mirror of
https://github.com/simonbrunel/qtpromise.git
synced 2024-11-22 02:34:30 +08:00
New documentation based on GitBook CLI
Split the root README.md in multiple Markdown files (in the `docs/` folder) to make easier reading, editing and extending the documentation. An online version is also available on netlify (https://qtpromise.netlify.com). Building it requires Node.js installed, then: - npm install -g gitbook-cli - gitbook install ./ - gitbook build . dist/docs
This commit is contained in:
parent
c34316243e
commit
18739bd8e0
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,3 +1,5 @@
|
||||
dist
|
||||
node_modules
|
||||
*.gcno
|
||||
*.gcda
|
||||
*.moc
|
||||
@ -9,4 +11,5 @@ Makefile*
|
||||
moc_*.cpp
|
||||
moc_*.h
|
||||
coverage.info
|
||||
package-lock.json
|
||||
target_wrapper.bat
|
||||
|
@ -30,3 +30,7 @@ script:
|
||||
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash) -f coverage.info
|
||||
|
||||
|
||||
# gitbook install .
|
||||
# gitbook build . dist/docs
|
||||
|
429
README.md
429
README.md
@ -1,433 +1,18 @@
|
||||
<a href="https://promisesaplus.com/" title="Promises/A+ 1.1"><img src="http://promisesaplus.com/assets/logo-small.png" alt="Promises/A+" align="right"/></a>
|
||||
<a href="https://promisesaplus.com/" title="Promises/A+ 1.1"><img src="https://promisesaplus.com/assets/logo-small.png" alt="Promises/A+" align="right"/></a>
|
||||
|
||||
# QtPromise
|
||||
[![qpm](https://img.shields.io/github/release/simonbrunel/qtpromise.svg?style=flat-square&label=qpm&colorB=4CAF50)](http://www.qpm.io/packages/com.github.simonbrunel.qtpromise/index.html) [![Travis](https://img.shields.io/travis/simonbrunel/qtpromise/master.svg?style=flat-square)](https://travis-ci.org/simonbrunel/qtpromise) [![coverage](https://img.shields.io/codecov/c/github/simonbrunel/qtpromise.svg?style=flat-square)](https://codecov.io/gh/simonbrunel/qtpromise)
|
||||
[![qpm](https://img.shields.io/github/release/simonbrunel/qtpromise.svg?style=flat-square&label=qpm&colorB=4CAF50)](https://www.qpm.io/packages/com.github.simonbrunel.qtpromise/index.html) [![Travis](https://img.shields.io/travis/simonbrunel/qtpromise/master.svg?style=flat-square)](https://travis-ci.org/simonbrunel/qtpromise) [![coverage](https://img.shields.io/codecov/c/github/simonbrunel/qtpromise.svg?style=flat-square)](https://codecov.io/gh/simonbrunel/qtpromise)
|
||||
|
||||
[Promises/A+](https://promisesaplus.com/) implementation for [Qt/C++](https://www.qt.io/).
|
||||
|
||||
Requires [Qt 5.4](https://www.qt.io/download/) (or later) with [C++11 support enabled](https://wiki.qt.io/How_to_use_C++11_in_your_Qt_Projects).
|
||||
|
||||
## Getting Started
|
||||
### Installation
|
||||
QtPromise is a [header-only](https://en.wikipedia.org/wiki/Header-only) library, simply download the [latest release](https://github.com/simonbrunel/qtpromise/releases/latest) (or [`git submodule`](https://git-scm.com/docs/git-submodule])) and include `qtpromise.pri` from your project `.pro`.
|
||||
## Documentation
|
||||
|
||||
### qpm
|
||||
Alternatively and **only** if your project relies on [qpm](http://www.qpm.io/), you can install QtPromise as follow:
|
||||
|
||||
```bash
|
||||
qpm install com.github.simonbrunel.qtpromise
|
||||
```
|
||||
|
||||
### Usage
|
||||
The recommended way to use QtPromise is to include the single module header:
|
||||
|
||||
```cpp
|
||||
#include <QtPromise>
|
||||
```
|
||||
|
||||
### Example
|
||||
Let's first make the code more readable by using the library namespace:
|
||||
|
||||
```cpp
|
||||
using namespace QtPromise;
|
||||
```
|
||||
|
||||
This `download` function creates a [promise from callbacks](#qpromise-qpromise) which will be resolved when the network request is finished:
|
||||
|
||||
```cpp
|
||||
QPromise<QByteArray> download(const QUrl& url)
|
||||
{
|
||||
return QPromise<QByteArray>([&](
|
||||
const QPromiseResolve<QByteArray>& resolve,
|
||||
const QPromiseReject<QByteArray>& reject) {
|
||||
|
||||
QNetworkReply* reply = manager->get(QNetworkRequest(url));
|
||||
QObject::connect(reply, &QNetworkReply::finished, [=]() {
|
||||
if (reply->error() == QNetworkReply::NoError) {
|
||||
resolve(reply->readAll());
|
||||
} else {
|
||||
reject(reply->error());
|
||||
}
|
||||
|
||||
reply->deleteLater();
|
||||
});
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
The following method `uncompress` data in a separate thread and returns a [promise from QFuture](#qtconcurrent):
|
||||
|
||||
```cpp
|
||||
QPromise<Entries> uncompress(const QByteArray& data)
|
||||
{
|
||||
return qPromise(QtConcurrent::run([](const QByteArray& data) {
|
||||
Entries entries;
|
||||
|
||||
// {...} uncompress data and parse content.
|
||||
|
||||
if (error) {
|
||||
throw MalformedException();
|
||||
}
|
||||
|
||||
return entries;
|
||||
}, data));
|
||||
}
|
||||
```
|
||||
|
||||
It's then easy to chain the whole asynchronous process using promises:
|
||||
- initiate the promise chain by downloading a specific URL,
|
||||
- [`then`](#qpromise-then) *and only if download succeeded*, uncompress received data,
|
||||
- [`then`](#qpromise-then) validate and process the uncompressed entries,
|
||||
- [`finally`](#qpromise-finally) perform operations whatever the process succeeded or failed,
|
||||
- and handle specific errors using [`fail`](#qpromise-fail).
|
||||
|
||||
```cpp
|
||||
download(url).then(&uncompress).then([](const Entries& entries) {
|
||||
if (entries.isEmpty()) {
|
||||
throw UpdateException("No entries");
|
||||
}
|
||||
// {...} process entries
|
||||
}).finally([]() {
|
||||
// {...} cleanup
|
||||
}).fail([](QNetworkReply::NetworkError err) {
|
||||
// {...} handle network error
|
||||
}).fail([](const UpdateException& err) {
|
||||
// {...} handle update error
|
||||
}).fail([]() {
|
||||
// {...} catch all
|
||||
});
|
||||
```
|
||||
|
||||
## QtConcurrent
|
||||
QtPromise integrates with [QtConcurrent](http://doc.qt.io/qt-5/qtconcurrent-index.html) to make easy chaining QFuture with QPromise.
|
||||
|
||||
### <a name="qtconcurrent-convert"></a> Convert
|
||||
Converting `QFuture<T>` to `QPromise<T>` is done using the [`qPromise`](#helpers-qpromise) helper:
|
||||
|
||||
```cpp
|
||||
QFuture<int> future = QtConcurrent::run([]() {
|
||||
// {...}
|
||||
return 42;
|
||||
});
|
||||
|
||||
QPromise<int> promise = qPromise(future);
|
||||
```
|
||||
|
||||
or simply:
|
||||
|
||||
```cpp
|
||||
auto promise = qPromise(QtConcurrent::run([]() {
|
||||
// {...}
|
||||
}));
|
||||
```
|
||||
|
||||
### Chain
|
||||
Returning a `QFuture<T>` in [`then`](#qpromise-then) or [`fail`](#qpromise-fail) automatically translate to `QPromise<T>`:
|
||||
|
||||
```cpp
|
||||
QPromise<int> input = ...
|
||||
auto output = input.then([](int res) {
|
||||
return QtConcurrent::run([]() {
|
||||
// {...}
|
||||
return QString("42");
|
||||
});
|
||||
});
|
||||
|
||||
// output type: QPromise<QString>
|
||||
output.then([](const QString& res) {
|
||||
// {...}
|
||||
});
|
||||
```
|
||||
|
||||
The `output` promise is resolved when the `QFuture` is [finished](http://doc.qt.io/qt-5/qfuture.html#isFinished).
|
||||
|
||||
### Error
|
||||
Exceptions thrown from a QtConcurrent thread reject the associated promise with the exception as the reason. Note that if you throw an exception that is not a subclass of `QException`, the promise with be rejected with [`QUnhandledException`](http://doc.qt.io/qt-5/qunhandledexception.html#details) (this restriction only applies to exceptions thrown from a QtConcurrent thread, [read more](http://doc.qt.io/qt-5/qexception.html#details)).
|
||||
|
||||
```cpp
|
||||
QPromise<int> promise = ...
|
||||
promise.then([](int res) {
|
||||
return QtConcurrent::run([]() {
|
||||
// {...}
|
||||
|
||||
if (!success) {
|
||||
throw CustomException();
|
||||
}
|
||||
|
||||
return QString("42");
|
||||
});
|
||||
}).fail(const CustomException& err) {
|
||||
// {...}
|
||||
});
|
||||
```
|
||||
|
||||
## Thread-Safety
|
||||
QPromise is thread-safe and can be copied and accessed across different threads. QPromise relies on [explicitly data sharing](http://doc.qt.io/qt-5/qexplicitlyshareddatapointer.html#details) and thus `auto p2 = p1` represents the same promise: when `p1` resolves, handlers registered on `p1` and `p2` are called, the fulfilled value being shared between both instances.
|
||||
|
||||
> **Note:** while it's safe to access the resolved value from different threads using [`then`](#qpromise-then), QPromise provides no guarantee about the object being pointed to. Thread-safety and reentrancy rules for that object still apply.
|
||||
|
||||
## QPromise
|
||||
### <a name="qpromise-qpromise"></a> `QPromise<T>::QPromise(resolver)`
|
||||
Creates a new promise that will be fulfilled or rejected by the given `resolver` lambda:
|
||||
|
||||
```cpp
|
||||
QPromise<int> promise([](const QPromiseResolve<int>& resolve, const QPromiseReject<int>& reject) {
|
||||
async_method([=](bool success, int result) {
|
||||
if (success) {
|
||||
resolve(result);
|
||||
} else {
|
||||
reject(customException());
|
||||
}
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
> **Note:** `QPromise<void>` is specialized to not contain any value, meaning that the `resolve` callback takes no argument.
|
||||
|
||||
**C++14**
|
||||
|
||||
```cpp
|
||||
QPromise<int> promise([](const auto& resolve, const auto& reject) {
|
||||
// {...}
|
||||
});
|
||||
```
|
||||
|
||||
### <a name="qpromise-then"></a> `QPromise<T>::then(onFulfilled, onRejected) -> QPromise<R>`
|
||||
See [Promises/A+ `.then`](https://promisesaplus.com/#the-then-method) for details.
|
||||
|
||||
```cpp
|
||||
QPromise<int> input = ...
|
||||
auto output = input.then([](int res) {
|
||||
// called with the 'input' result if fulfilled
|
||||
}, [](const ReasonType& reason) {
|
||||
// called with the 'input' reason if rejected
|
||||
// see QPromise<T>::fail for details
|
||||
});
|
||||
```
|
||||
|
||||
> **Note**: `onRejected` handler is optional, `output` will be rejected with the same reason as `input`.
|
||||
|
||||
> **Note**: it's recommended to use the [`fail`](#qpromise-fail) shorthand to handle errors.
|
||||
|
||||
The type `<R>` of the `output` promise depends on the return type of the `onFulfilled` handler:
|
||||
|
||||
```cpp
|
||||
QPromise<int> input = {...}
|
||||
auto output = input.then([](int res) {
|
||||
return QString::number(res); // -> QPromise<QString>
|
||||
});
|
||||
|
||||
// output type: QPromise<QString>
|
||||
output.then([](const QString& res) {
|
||||
// {...}
|
||||
});
|
||||
```
|
||||
|
||||
> **Note**: only `onFulfilled` can change the promise type, `onRejected` **must** return the same type as `onFulfilled`. That also means if `onFulfilled` is `nullptr`, `onRejected` must return the same type as the `input` promise.
|
||||
|
||||
```cpp
|
||||
QPromise<int> input = ...
|
||||
auto output = input.then([](int res) {
|
||||
return res + 4;
|
||||
}, [](const ReasonType& reason) {
|
||||
return -1;
|
||||
});
|
||||
```
|
||||
|
||||
If `onFulfilled` doesn't return any value, the `output` type is `QPromise<void>`:
|
||||
|
||||
```cpp
|
||||
QPromise<int> input = ...
|
||||
auto output = input.then([](int res) {
|
||||
// {...}
|
||||
});
|
||||
|
||||
// output type: QPromise<void>
|
||||
output.then([]() {
|
||||
// `QPromise<void>` `onFulfilled` handler has no argument
|
||||
});
|
||||
```
|
||||
|
||||
You can also decide to skip the promise result by omitting the handler argument:
|
||||
|
||||
```cpp
|
||||
QPromise<int> input = {...}
|
||||
auto output = input.then([]( /* skip int result */ ) {
|
||||
// {...}
|
||||
});
|
||||
```
|
||||
|
||||
The `output` promise can be *rejected* by throwing an exception in either `onFulfilled` or `onRejected`:
|
||||
|
||||
```cpp
|
||||
QPromise<int> input = {...}
|
||||
auto output = input.then([](int res) {
|
||||
if (res == -1) {
|
||||
throw ReasonType();
|
||||
} else {
|
||||
return res;
|
||||
}
|
||||
});
|
||||
|
||||
// output.isRejected() is true
|
||||
```
|
||||
|
||||
If an handler returns a promise (or QFuture), the `output` promise is delayed and will be resolved by the returned promise.
|
||||
|
||||
### <a name="qpromise-fail"></a> `QPromise<T>::fail(onRejected) -> QPromise<T>`
|
||||
Shorthand to `promise.then(nullptr, onRejected)`, similar to the [`catch` statement](http://en.cppreference.com/w/cpp/language/try_catch):
|
||||
|
||||
```cpp
|
||||
promise.fail([](const MyException&) {
|
||||
// {...}
|
||||
}).fail(const QException&) {
|
||||
// {...}
|
||||
}).fail(const std::exception&) {
|
||||
// {...}
|
||||
}).fail() {
|
||||
// {...} catch-all
|
||||
});
|
||||
```
|
||||
|
||||
### <a name="qpromise-finally"></a> `QPromise<T>::finally(handler) -> QPromise<T>`
|
||||
This `handler` is **always** called, without any argument and whatever the `input` promise state (fulfilled or rejected). The `output` promise has the same type as the `input` one but also the same value or error. The finally `handler` **can not modify the fulfilled value** (the returned value is ignored), however, if `handler` throws, `output` is rejected with the new exception.
|
||||
|
||||
```cpp
|
||||
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.
|
||||
|
||||
### <a name="qpromise-tap"></a> `QPromise<T>::tap(handler) -> QPromise<T>`
|
||||
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<int> input = {...}
|
||||
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.
|
||||
|
||||
### <a name="qpromise-delay"></a> `QPromise<T>::delay(handler) -> QPromise<T>`
|
||||
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<int> input = {...}
|
||||
auto output = input.delay(2000).then([](int res) {
|
||||
// called 2 seconds after `input` is fulfilled
|
||||
});
|
||||
```
|
||||
|
||||
### <a name="qpromise-wait"></a> `QPromise<T>::wait() -> QPromise<T>`
|
||||
This method holds the execution of the remaining code **without** blocking the event loop of the current thread:
|
||||
|
||||
```cpp
|
||||
int result = -1;
|
||||
QPromise<int> input = qPromise(QtConcurrent::run([]() { return 42; }));
|
||||
auto output = input.then([&](int res) {
|
||||
result = res;
|
||||
});
|
||||
|
||||
// output.isPending() is true && result is -1
|
||||
|
||||
output.wait();
|
||||
|
||||
// output.isPending() is false && result is 42
|
||||
```
|
||||
|
||||
### `QPromise<T>::isPending() -> bool`
|
||||
Returns `true` if the promise is pending (not fulfilled or rejected), otherwise returns `false`.
|
||||
|
||||
### `QPromise<T>::isFulfilled() -> bool`
|
||||
Returns `true` if the promise is fulfilled, otherwise returns `false`.
|
||||
|
||||
### `QPromise<T>::isRejected() -> bool`
|
||||
Returns `true` if the promise is rejected, otherwise returns `false`.
|
||||
|
||||
## QPromise (statics)
|
||||
### <a name="qpromise-resolve"></a> `[static] QPromise<T>::resolve(value) -> QPromise<T>`
|
||||
Creates a `QPromise<T>` that is fulfilled with the given `value` of type `T`:
|
||||
|
||||
```cpp
|
||||
QPromise<int> compute(const QString& type)
|
||||
{
|
||||
if (type == "magic") {
|
||||
return QPromise<int>::resolve(42);
|
||||
}
|
||||
|
||||
return QPromise<int>([](const QPromiseResolve<int>& resolve) {
|
||||
// {...}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
See also: [`qPromise`](#helpers-qpromise)
|
||||
|
||||
### <a name="qpromise-reject"></a> `[static] QPromise<T>::reject(reason) -> QPromise<T>`
|
||||
Creates a `QPromise<T>` that is rejected with the given `reason` of *whatever type*:
|
||||
|
||||
```cpp
|
||||
QPromise<int> compute(const QString& type)
|
||||
{
|
||||
if (type == "foobar") {
|
||||
return QPromise<int>::reject(QString("Unknown type: %1").arg(type));
|
||||
}
|
||||
|
||||
return QPromise<int>([](const QPromiseResolve<int>& resolve) {
|
||||
// {...}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### <a name="qpromise-all"></a> `[static] QPromise<T>::all(Sequence<QPromise<T>>) -> QPromise<QVector<T>>`
|
||||
Returns a `QPromise<QVector<T>>` that fulfills when **all** `promises` of (the same) type `T` have been fulfilled. The `output` value is a vector containing **all** the values of `promises`, in the same order. If any of the given `promises` fail, `output` immediately rejects with the error of the promise that rejected, whether or not the other promises are resolved.
|
||||
|
||||
`Sequence` is any STL compatible container (eg. `QVector`, `QList`, `std::vector`, etc.)
|
||||
|
||||
```cpp
|
||||
QVector<QPromise<QByteArray> > promises{
|
||||
download(QUrl("http://a...")),
|
||||
download(QUrl("http://b...")),
|
||||
download(QUrl("http://c..."))
|
||||
};
|
||||
|
||||
auto output = QPromise<QByteArray>::all(promises);
|
||||
|
||||
// output type: QPromise<QVector<QByteArray>>
|
||||
output.then([](const QVector<QByteArray>& res) {
|
||||
// {...}
|
||||
});
|
||||
```
|
||||
|
||||
See also: [`qPromiseAll`](#helpers-qpromiseall)
|
||||
|
||||
## Helpers
|
||||
### <a name="helpers-qpromise"></a> `qPromise(T value) -> QPromise<R>`
|
||||
Similar to the `QPromise<T>::resolve` static method, creates a promise resolved from a given `value` without the extra typing:
|
||||
|
||||
```cpp
|
||||
auto promise = qPromise(); // QPromise<void>
|
||||
auto promise = qPromise(42); // QPromise<int>
|
||||
auto promise = qPromise(QString("foo")); // QPromise<QString>
|
||||
```
|
||||
|
||||
This method also allows to convert `QFuture<T>` to `QPromise<T>` delayed until the `QFuture` is finished ([read more](#qtconcurrent-convert)).
|
||||
|
||||
### <a name="helpers-qpromiseall"></a> `qPromiseAll(Sequence<QPromise<T> promises) -> QPromise<QVector<T>>`
|
||||
This method simply calls the appropriated [`QPromise<T>::all`](#qpromise-all) static method based on the given `QVector` type. In some cases, this method is more convenient than the static one since it avoid some extra typing:
|
||||
|
||||
```cpp
|
||||
QVector<QPromise<QByteArray> > promises{...}
|
||||
|
||||
auto output = qPromiseAll(promises);
|
||||
// eq. QPromise<QByteArray>::all(promises)
|
||||
```
|
||||
* [Getting Started](https://qtpromise.netlify.com/qtpromise/getting-started)
|
||||
* [Thread-Safety](https://qtpromise.netlify.com/qtpromise/thread-safety)
|
||||
* [QtConcurrent](https://qtpromise.netlify.com/qtpromise/qtconcurrent)
|
||||
* [API Reference](https://qtpromise.netlify.com/qtpromise/api-reference)
|
||||
|
||||
## License
|
||||
QtPromise is available under the [MIT license](LICENSE).
|
||||
|
40
book.json
Normal file
40
book.json
Normal file
@ -0,0 +1,40 @@
|
||||
{
|
||||
"title": "QtPromise",
|
||||
"description": "Promises/A+ implementation for Qt/C++",
|
||||
"author": "Simon Brunel",
|
||||
"gitbook": "3.2.3",
|
||||
"root": "docs",
|
||||
"plugins": [
|
||||
"-lunr",
|
||||
"-search",
|
||||
"search-plus",
|
||||
"anchorjs",
|
||||
"edit-link",
|
||||
"expand-active-chapter",
|
||||
"ga",
|
||||
"github"
|
||||
],
|
||||
"pluginsConfig": {
|
||||
"anchorjs": {
|
||||
"icon": "#",
|
||||
"placement": "left",
|
||||
"visible": "always"
|
||||
},
|
||||
"edit-link": {
|
||||
"base": "https://github.com/simonbrunel/qtpromise/edit/master/docs"
|
||||
},
|
||||
"ga": {
|
||||
"token": "UA-113899811-1",
|
||||
"configuration": "auto"
|
||||
},
|
||||
"github": {
|
||||
"url": "https://github.com/simonbrunel/qtpromise"
|
||||
},
|
||||
"theme-default": {
|
||||
"showLevel": false,
|
||||
"styles": {
|
||||
"website": "assets/style.css"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
15
docs/README.md
Normal file
15
docs/README.md
Normal file
@ -0,0 +1,15 @@
|
||||
<a href="https://promisesaplus.com/" title="Promises/A+ 1.1"><img src="https://promisesaplus.com/assets/logo-small.png" alt="Promises/A+" align="right"/></a>
|
||||
|
||||
# QtPromise
|
||||
[Promises/A+](https://promisesaplus.com/) implementation for [Qt/C++](https://www.qt.io/).
|
||||
|
||||
Requires [Qt 5.4](https://www.qt.io/download/) (or later) with [C++11 support enabled](https://wiki.qt.io/How_to_use_C++11_in_your_Qt_Projects).
|
||||
|
||||
## QtPromise for C++
|
||||
* [Getting Started](qtpromise/getting-started.md)
|
||||
* [Thread-Safety](qtpromise/thread-safety.md)
|
||||
* [QtConcurrent](qtpromise/qtconcurrent.md)
|
||||
* [API Reference](qtpromise/api-reference.md)
|
||||
|
||||
## License
|
||||
QtPromise is available under the [MIT license](https://github.com/simonbrunel/qtpromise/blob/master/LICENSE).
|
20
docs/SUMMARY.md
Normal file
20
docs/SUMMARY.md
Normal file
@ -0,0 +1,20 @@
|
||||
### QtPromise for C++
|
||||
* [Getting Started](qtpromise/getting-started.md)
|
||||
* [QtConcurrent](qtpromise/qtconcurrent.md)
|
||||
* [Thread-Safety](qtpromise/thread-safety.md)
|
||||
* [API Reference](qtpromise/api-reference.md)
|
||||
* [QPromise](qtpromise/qpromise/constructor.md)
|
||||
* [.delay](qtpromise/qpromise/delay.md)
|
||||
* [.fail](qtpromise/qpromise/fail.md)
|
||||
* [.finally](qtpromise/qpromise/finally.md)
|
||||
* [.isFulfilled](qtpromise/qpromise/isfulfilled.md)
|
||||
* [.isPending](qtpromise/qpromise/ispending.md)
|
||||
* [.isRejected](qtpromise/qpromise/isrejected.md)
|
||||
* [.tap](qtpromise/qpromise/tap.md)
|
||||
* [.then](qtpromise/qpromise/then.md)
|
||||
* [.wait](qtpromise/qpromise/wait.md)
|
||||
* [::all (static)](qtpromise/qpromise/all.md)
|
||||
* [::reject (static)](qtpromise/qpromise/reject.md)
|
||||
* [::resolve (static)](qtpromise/qpromise/resolve.md)
|
||||
* [qPromise](qtpromise/helpers/qpromise.md)
|
||||
* [qPromiseAll](qtpromise/helpers/qpromiseall.md)
|
15
docs/assets/style.css
Normal file
15
docs/assets/style.css
Normal file
@ -0,0 +1,15 @@
|
||||
a.anchorjs-link {
|
||||
color: rgba(65, 131, 196, 0.1);
|
||||
font-weight: 400;
|
||||
text-decoration: none;
|
||||
transition: color 100ms ease-out;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
a.anchorjs-link:hover {
|
||||
color: rgba(65, 131, 196, 1);
|
||||
}
|
||||
|
||||
sup {
|
||||
font-size: 0.75em !important;
|
||||
}
|
25
docs/qtpromise/api-reference.md
Normal file
25
docs/qtpromise/api-reference.md
Normal file
@ -0,0 +1,25 @@
|
||||
## QPromise
|
||||
|
||||
### Public Members
|
||||
|
||||
* [`QPromise<T>::QPromise`](qpromise/constructor.md)
|
||||
* [`QPromise<T>::delay`](qpromise/delay.md)
|
||||
* [`QPromise<T>::fail`](qpromise/fail.md)
|
||||
* [`QPromise<T>::finally`](qpromise/finally.md)
|
||||
* [`QPromise<T>::isFulfilled`](qpromise/isfulfilled.md)
|
||||
* [`QPromise<T>::isPending`](qpromise/ispending.md)
|
||||
* [`QPromise<T>::isRejected`](qpromise/isrejected.md)
|
||||
* [`QPromise<T>::tap`](qpromise/tap.md)
|
||||
* [`QPromise<T>::then`](qpromise/then.md)
|
||||
* [`QPromise<T>::wait`](qpromise/wait.md)
|
||||
|
||||
### Public Static Members
|
||||
|
||||
* [`[static] QPromise<T>::all`](qpromise/all.md)
|
||||
* [`[static] QPromise<T>::reject`](qpromise/reject.md)
|
||||
* [`[static] QPromise<T>::resolve`](qpromise/resolve.md)
|
||||
|
||||
## Helpers
|
||||
|
||||
* [`qPromise`](helpers/qpromise.md)
|
||||
* [`qPromiseAll`](helpers/qpromiseall.md)
|
93
docs/qtpromise/getting-started.md
Normal file
93
docs/qtpromise/getting-started.md
Normal file
@ -0,0 +1,93 @@
|
||||
## Installation
|
||||
|
||||
QtPromise is a [header-only](https://en.wikipedia.org/wiki/Header-only) library, simply download the [latest release](https://github.com/simonbrunel/qtpromise/releases/latest) (or [`git submodule`](https://git-scm.com/docs/git-submodule)) and include `qtpromise.pri` from your project `.pro`.
|
||||
|
||||
## qpm
|
||||
|
||||
Alternatively and **only** if your project relies on [qpm](https://www.qpm.io/), you can install QtPromise as follow:
|
||||
|
||||
```bash
|
||||
qpm install com.github.simonbrunel.qtpromise
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
The recommended way to use QtPromise is to include the single module header:
|
||||
|
||||
```cpp
|
||||
#include <QtPromise>
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
Let's first make the code more readable by using the library namespace:
|
||||
|
||||
```cpp
|
||||
using namespace QtPromise;
|
||||
```
|
||||
|
||||
This `download` function creates a [promise from callbacks](qpromise/constructor.md) which will be resolved when the network request is finished:
|
||||
|
||||
```cpp
|
||||
QPromise<QByteArray> download(const QUrl& url)
|
||||
{
|
||||
return QPromise<QByteArray>([&](
|
||||
const QPromiseResolve<QByteArray>& resolve,
|
||||
const QPromiseReject<QByteArray>& reject) {
|
||||
|
||||
QNetworkReply* reply = manager->get(QNetworkRequest(url));
|
||||
QObject::connect(reply, &QNetworkReply::finished, [=]() {
|
||||
if (reply->error() == QNetworkReply::NoError) {
|
||||
resolve(reply->readAll());
|
||||
} else {
|
||||
reject(reply->error());
|
||||
}
|
||||
|
||||
reply->deleteLater();
|
||||
});
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
The following method `uncompress` data in a separate thread and returns a [promise from QFuture](qtconcurrent.md):
|
||||
|
||||
```cpp
|
||||
QPromise<Entries> uncompress(const QByteArray& data)
|
||||
{
|
||||
return qPromise(QtConcurrent::run([](const QByteArray& data) {
|
||||
Entries entries;
|
||||
|
||||
// {...} uncompress data and parse content.
|
||||
|
||||
if (error) {
|
||||
throw MalformedException();
|
||||
}
|
||||
|
||||
return entries;
|
||||
}, data));
|
||||
}
|
||||
```
|
||||
|
||||
It's then easy to chain the whole asynchronous process using promises:
|
||||
- initiate the promise chain by downloading a specific URL,
|
||||
- [`then`](qpromise/then.md) *and only if download succeeded*, uncompress received data,
|
||||
- [`then`](qpromise/then.md) validate and process the uncompressed entries,
|
||||
- [`finally`](qpromise/finally.md) perform operations whatever the process succeeded or failed,
|
||||
- and handle specific errors using [`fail`](qpromise/fail.md).
|
||||
|
||||
```cpp
|
||||
download(url).then(&uncompress).then([](const Entries& entries) {
|
||||
if (entries.isEmpty()) {
|
||||
throw UpdateException("No entries");
|
||||
}
|
||||
// {...} process entries
|
||||
}).finally([]() {
|
||||
// {...} cleanup
|
||||
}).fail([](QNetworkReply::NetworkError err) {
|
||||
// {...} handle network error
|
||||
}).fail([](const UpdateException& err) {
|
||||
// {...} handle update error
|
||||
}).fail([]() {
|
||||
// {...} catch all
|
||||
});
|
||||
```
|
15
docs/qtpromise/helpers/qpromise.md
Normal file
15
docs/qtpromise/helpers/qpromise.md
Normal file
@ -0,0 +1,15 @@
|
||||
## `qPromise`
|
||||
|
||||
```
|
||||
qPromise(T value) -> QPromise<R>
|
||||
```
|
||||
|
||||
Similar to the [`QPromise<T>::resolve`](../qpromise/resolve.md) static method, creates a promise resolved from a given `value` without the extra typing:
|
||||
|
||||
```cpp
|
||||
auto promise = qPromise(); // QPromise<void>
|
||||
auto promise = qPromise(42); // QPromise<int>
|
||||
auto promise = qPromise(QString("foo")); // QPromise<QString>
|
||||
```
|
||||
|
||||
This method also allows to convert `QFuture<T>` to `QPromise<T>` delayed until the `QFuture` is finished ([read more](../qtconcurrent.md#convert)).
|
16
docs/qtpromise/helpers/qpromiseall.md
Normal file
16
docs/qtpromise/helpers/qpromiseall.md
Normal file
@ -0,0 +1,16 @@
|
||||
## `qPromiseAll`
|
||||
|
||||
```
|
||||
qPromiseAll(Sequence<QPromise<T>> promises) -> QPromise<QVector<T>>
|
||||
qPromiseAll(Sequence<QPromise<void>> promises) -> QPromise<void>
|
||||
```
|
||||
|
||||
This method simply calls the appropriated [`QPromise<T>::all`](../qpromise/all.md) static method based on the given `QVector` type. In some cases, this method is more convenient than the static one since it avoid some extra typing:
|
||||
|
||||
```cpp
|
||||
QVector<QPromise<QByteArray> > promises{...}
|
||||
|
||||
auto output = qPromiseAll(promises);
|
||||
// eq. QPromise<QByteArray>::all(promises)
|
||||
```
|
||||
|
26
docs/qtpromise/qpromise/all.md
Normal file
26
docs/qtpromise/qpromise/all.md
Normal file
@ -0,0 +1,26 @@
|
||||
## `[static] QPromise<T>::all`
|
||||
|
||||
```
|
||||
[static] QPromise<T>::all(Sequence<QPromise<T>> promises) -> QPromise<QVector<T>>
|
||||
```
|
||||
|
||||
Returns a `QPromise<QVector<T>>` that fulfills when **all** `promises` of (the same) type `T` have been fulfilled. The `output` value is a vector containing **all** the values of `promises`, in the same order. If any of the given `promises` fail, `output` immediately rejects with the error of the promise that rejected, whether or not the other promises are resolved.
|
||||
|
||||
`Sequence` is any STL compatible container (eg. `QVector`, `QList`, `std::vector`, etc.)
|
||||
|
||||
```cpp
|
||||
QVector<QPromise<QByteArray> > promises{
|
||||
download(QUrl("http://a...")),
|
||||
download(QUrl("http://b...")),
|
||||
download(QUrl("http://c..."))
|
||||
};
|
||||
|
||||
auto output = QPromise<QByteArray>::all(promises);
|
||||
|
||||
// output type: QPromise<QVector<QByteArray>>
|
||||
output.then([](const QVector<QByteArray>& res) {
|
||||
// {...}
|
||||
});
|
||||
```
|
||||
|
||||
See also: [`qPromiseAll`](../helpers/qpromiseall.md)
|
29
docs/qtpromise/qpromise/constructor.md
Normal file
29
docs/qtpromise/qpromise/constructor.md
Normal file
@ -0,0 +1,29 @@
|
||||
## `QPromise<T>::QPromise`
|
||||
|
||||
```
|
||||
QPromise<T>::QPromise(Function resolver)
|
||||
```
|
||||
|
||||
Creates a new promise that will be fulfilled or rejected by the given `resolver` lambda:
|
||||
|
||||
```cpp
|
||||
QPromise<int> promise([](const QPromiseResolve<int>& resolve, const QPromiseReject<int>& reject) {
|
||||
async_method([=](bool success, int result) {
|
||||
if (success) {
|
||||
resolve(result);
|
||||
} else {
|
||||
reject(customException());
|
||||
}
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
> **Note:** `QPromise<void>` is specialized to not contain any value, meaning that the `resolve` callback takes no argument.
|
||||
|
||||
**C++14**
|
||||
|
||||
```cpp
|
||||
QPromise<int> promise([](const auto& resolve, const auto& reject) {
|
||||
// {...}
|
||||
});
|
||||
```
|
14
docs/qtpromise/qpromise/delay.md
Normal file
14
docs/qtpromise/qpromise/delay.md
Normal file
@ -0,0 +1,14 @@
|
||||
## `QPromise<T>::delay`
|
||||
|
||||
```
|
||||
QPromise<T>::delay(int msec) -> QPromise<T>
|
||||
```
|
||||
|
||||
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<int> input = {...}
|
||||
auto output = input.delay(2000).then([](int res) {
|
||||
// called 2 seconds after `input` is fulfilled
|
||||
});
|
||||
```
|
19
docs/qtpromise/qpromise/fail.md
Normal file
19
docs/qtpromise/qpromise/fail.md
Normal file
@ -0,0 +1,19 @@
|
||||
## `QPromise<T>::fail`
|
||||
|
||||
```
|
||||
QPromise<T>::fail(Function onRejected) -> QPromise<T>
|
||||
```
|
||||
|
||||
Shorthand to `promise.then(nullptr, onRejected)`, similar to the [`catch` statement](http://en.cppreference.com/w/cpp/language/try_catch):
|
||||
|
||||
```cpp
|
||||
promise.fail([](const MyException&) {
|
||||
// {...}
|
||||
}).fail(const QException&) {
|
||||
// {...}
|
||||
}).fail(const std::exception&) {
|
||||
// {...}
|
||||
}).fail() {
|
||||
// {...} catch-all
|
||||
});
|
||||
```
|
15
docs/qtpromise/qpromise/finally.md
Normal file
15
docs/qtpromise/qpromise/finally.md
Normal file
@ -0,0 +1,15 @@
|
||||
## `QPromise<T>::finally`
|
||||
|
||||
```
|
||||
QPromise<T>::finally(Function handler) -> QPromise<T>
|
||||
```
|
||||
|
||||
This `handler` is **always** called, without any argument and whatever the `input` promise state (fulfilled or rejected). The `output` promise has the same type as the `input` one but also the same value or error. The finally `handler` **can not modify the fulfilled value** (the returned value is ignored), however, if `handler` throws, `output` is rejected with the new exception.
|
||||
|
||||
```cpp
|
||||
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.
|
7
docs/qtpromise/qpromise/isfulfilled.md
Normal file
7
docs/qtpromise/qpromise/isfulfilled.md
Normal file
@ -0,0 +1,7 @@
|
||||
# `QPromise<T>::isFulfilled`
|
||||
|
||||
```
|
||||
QPromise<T>::isFulfilled() -> bool
|
||||
```
|
||||
|
||||
Returns `true` if the promise is fulfilled, otherwise returns `false`.
|
7
docs/qtpromise/qpromise/ispending.md
Normal file
7
docs/qtpromise/qpromise/ispending.md
Normal file
@ -0,0 +1,7 @@
|
||||
# `QPromise<T>::isPending`
|
||||
|
||||
```
|
||||
QPromise<T>::isPending() -> bool
|
||||
```
|
||||
|
||||
Returns `true` if the promise is pending (not fulfilled or rejected), otherwise returns `false`.
|
8
docs/qtpromise/qpromise/isrejected.md
Normal file
8
docs/qtpromise/qpromise/isrejected.md
Normal file
@ -0,0 +1,8 @@
|
||||
# `QPromise<T>::isRejected`
|
||||
|
||||
```
|
||||
QPromise<T>::isRejected() -> bool
|
||||
```
|
||||
|
||||
Returns `true` if the promise is rejected, otherwise returns `false`.
|
||||
|
20
docs/qtpromise/qpromise/reject.md
Normal file
20
docs/qtpromise/qpromise/reject.md
Normal file
@ -0,0 +1,20 @@
|
||||
## `[static] QPromise<T>::reject`
|
||||
|
||||
```
|
||||
[static] QPromise<T>::reject(any reason) -> QPromise<T>
|
||||
```
|
||||
|
||||
Creates a `QPromise<T>` that is rejected with the given `reason` of *whatever type*:
|
||||
|
||||
```cpp
|
||||
QPromise<int> compute(const QString& type)
|
||||
{
|
||||
if (type == "foobar") {
|
||||
return QPromise<int>::reject(QString("Unknown type: %1").arg(type));
|
||||
}
|
||||
|
||||
return QPromise<int>([](const QPromiseResolve<int>& resolve) {
|
||||
// {...}
|
||||
});
|
||||
}
|
||||
```
|
22
docs/qtpromise/qpromise/resolve.md
Normal file
22
docs/qtpromise/qpromise/resolve.md
Normal file
@ -0,0 +1,22 @@
|
||||
## `[static] QPromise<T>::resolve`
|
||||
|
||||
```
|
||||
[static] QPromise<T>::resolve(T value) -> QPromise<T>
|
||||
```
|
||||
|
||||
Creates a `QPromise<T>` that is fulfilled with the given `value` of type `T`:
|
||||
|
||||
```cpp
|
||||
QPromise<int> compute(const QString& type)
|
||||
{
|
||||
if (type == "magic") {
|
||||
return QPromise<int>::resolve(42);
|
||||
}
|
||||
|
||||
return QPromise<int>([](const QPromiseResolve<int>& resolve) {
|
||||
// {...}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
See also: [`qPromise`](../helpers/qpromise.md)
|
18
docs/qtpromise/qpromise/tap.md
Normal file
18
docs/qtpromise/qpromise/tap.md
Normal file
@ -0,0 +1,18 @@
|
||||
## `QPromise<T>::tap`
|
||||
|
||||
```
|
||||
QPromise<T>::tap(Function handler) -> QPromise<T>
|
||||
```
|
||||
|
||||
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`](finally.md), this handler is **not** called for rejections.
|
||||
|
||||
```cpp
|
||||
QPromise<int> input = {...}
|
||||
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.
|
87
docs/qtpromise/qpromise/then.md
Normal file
87
docs/qtpromise/qpromise/then.md
Normal file
@ -0,0 +1,87 @@
|
||||
## `QPromise<T>::then`
|
||||
|
||||
```
|
||||
QPromise<T>::then(Function onFulfilled, Function onRejected) -> QPromise<R>
|
||||
QPromise<T>::then(Function onFulfilled) -> QPromise<R>
|
||||
```
|
||||
|
||||
See [Promises/A+ `.then`](https://promisesaplus.com/#the-then-method) for details.
|
||||
|
||||
```cpp
|
||||
QPromise<int> input = ...
|
||||
auto output = input.then([](int res) {
|
||||
// called with the 'input' result if fulfilled
|
||||
}, [](const ReasonType& reason) {
|
||||
// called with the 'input' reason if rejected
|
||||
// see QPromise<T>::fail for details
|
||||
});
|
||||
```
|
||||
|
||||
> **Note**: `onRejected` handler is optional, `output` will be rejected with the same reason as `input`.
|
||||
|
||||
> **Note**: it's recommended to use the [`fail`](fail.md) shorthand to handle errors.
|
||||
|
||||
The type `<R>` of the `output` promise depends on the return type of the `onFulfilled` handler:
|
||||
|
||||
```cpp
|
||||
QPromise<int> input = {...}
|
||||
auto output = input.then([](int res) {
|
||||
return QString::number(res); // -> QPromise<QString>
|
||||
});
|
||||
|
||||
// output type: QPromise<QString>
|
||||
output.then([](const QString& res) {
|
||||
// {...}
|
||||
});
|
||||
```
|
||||
|
||||
> **Note**: only `onFulfilled` can change the promise type, `onRejected` **must** return the same type as `onFulfilled`. That also means if `onFulfilled` is `nullptr`, `onRejected` must return the same type as the `input` promise.
|
||||
|
||||
```cpp
|
||||
QPromise<int> input = ...
|
||||
auto output = input.then([](int res) {
|
||||
return res + 4;
|
||||
}, [](const ReasonType& reason) {
|
||||
return -1;
|
||||
});
|
||||
```
|
||||
|
||||
If `onFulfilled` doesn't return any value, the `output` type is `QPromise<void>`:
|
||||
|
||||
```cpp
|
||||
QPromise<int> input = ...
|
||||
auto output = input.then([](int res) {
|
||||
// {...}
|
||||
});
|
||||
|
||||
// output type: QPromise<void>
|
||||
output.then([]() {
|
||||
// `QPromise<void>` `onFulfilled` handler has no argument
|
||||
});
|
||||
```
|
||||
|
||||
You can also decide to skip the promise result by omitting the handler argument:
|
||||
|
||||
```cpp
|
||||
QPromise<int> input = {...}
|
||||
auto output = input.then([]( /* skip int result */ ) {
|
||||
// {...}
|
||||
});
|
||||
```
|
||||
|
||||
The `output` promise can be *rejected* by throwing an exception in either `onFulfilled` or `onRejected`:
|
||||
|
||||
```cpp
|
||||
QPromise<int> input = {...}
|
||||
auto output = input.then([](int res) {
|
||||
if (res == -1) {
|
||||
throw ReasonType();
|
||||
} else {
|
||||
return res;
|
||||
}
|
||||
});
|
||||
|
||||
// output.isRejected() is true
|
||||
```
|
||||
|
||||
If an handler returns a promise (or QFuture), the `output` promise is delayed and will be resolved by the returned promise.
|
21
docs/qtpromise/qpromise/wait.md
Normal file
21
docs/qtpromise/qpromise/wait.md
Normal file
@ -0,0 +1,21 @@
|
||||
## `QPromise<T>::wait`
|
||||
|
||||
```
|
||||
QPromise<T>::wait() -> QPromise<T>
|
||||
```
|
||||
|
||||
This method holds the execution of the remaining code **without** blocking the event loop of the current thread:
|
||||
|
||||
```cpp
|
||||
int result = -1;
|
||||
QPromise<int> input = qPromise(QtConcurrent::run([]() { return 42; }));
|
||||
auto output = input.then([&](int res) {
|
||||
result = res;
|
||||
});
|
||||
|
||||
// output.isPending() is true && result is -1
|
||||
|
||||
output.wait();
|
||||
|
||||
// output.isPending() is false && result is 42
|
||||
```
|
66
docs/qtpromise/qtconcurrent.md
Normal file
66
docs/qtpromise/qtconcurrent.md
Normal file
@ -0,0 +1,66 @@
|
||||
## QtConcurrent
|
||||
|
||||
QtPromise integrates with [QtConcurrent](https://doc.qt.io/qt-5/qtconcurrent-index.html) to make easy chaining QFuture with QPromise.
|
||||
|
||||
## <a name="qtconcurrent-convert"></a> Convert
|
||||
|
||||
Converting `QFuture<T>` to `QPromise<T>` is done using the [`qPromise`](helpers/qpromise.md) helper:
|
||||
|
||||
```cpp
|
||||
QFuture<int> future = QtConcurrent::run([]() {
|
||||
// {...}
|
||||
return 42;
|
||||
});
|
||||
|
||||
QPromise<int> promise = qPromise(future);
|
||||
```
|
||||
|
||||
or simply:
|
||||
|
||||
```cpp
|
||||
auto promise = qPromise(QtConcurrent::run([]() {
|
||||
// {...}
|
||||
}));
|
||||
```
|
||||
|
||||
## Chain
|
||||
|
||||
Returning a `QFuture<T>` in [`then`](qpromise/then.md) or [`fail`](qpromise/fail.md) automatically translate to `QPromise<T>`:
|
||||
|
||||
```cpp
|
||||
QPromise<int> input = ...
|
||||
auto output = input.then([](int res) {
|
||||
return QtConcurrent::run([]() {
|
||||
// {...}
|
||||
return QString("42");
|
||||
});
|
||||
});
|
||||
|
||||
// output type: QPromise<QString>
|
||||
output.then([](const QString& res) {
|
||||
// {...}
|
||||
});
|
||||
```
|
||||
|
||||
The `output` promise is resolved when the `QFuture` is [finished](https://doc.qt.io/qt-5/qfuture.html#isFinished).
|
||||
|
||||
## Error
|
||||
|
||||
Exceptions thrown from a QtConcurrent thread reject the associated promise with the exception as the reason. Note that if you throw an exception that is not a subclass of `QException`, the promise with be rejected with [`QUnhandledException`](https://doc.qt.io/qt-5/qunhandledexception.html#details) (this restriction only applies to exceptions thrown from a QtConcurrent thread, [read more](https://doc.qt.io/qt-5/qexception.html#details)).
|
||||
|
||||
```cpp
|
||||
QPromise<int> promise = ...
|
||||
promise.then([](int res) {
|
||||
return QtConcurrent::run([]() {
|
||||
// {...}
|
||||
|
||||
if (!success) {
|
||||
throw CustomException();
|
||||
}
|
||||
|
||||
return QString("42");
|
||||
});
|
||||
}).fail(const CustomException& err) {
|
||||
// {...}
|
||||
});
|
||||
```
|
5
docs/qtpromise/thread-safety.md
Normal file
5
docs/qtpromise/thread-safety.md
Normal file
@ -0,0 +1,5 @@
|
||||
## Thread-Safety
|
||||
|
||||
QPromise is thread-safe and can be copied and accessed across different threads. QPromise relies on [explicitly data sharing](https://doc.qt.io/qt-5/qexplicitlyshareddatapointer.html#details) and thus `auto p2 = p1` represents the same promise: when `p1` resolves, handlers registered on `p1` and `p2` are called, the fulfilled value being shared between both instances.
|
||||
|
||||
> **Note:** while it's safe to access the resolved value from different threads using [`then`](qpromise/then.md), QPromise provides no guarantee about the object being pointed to. Thread-safety and reentrancy rules for that object still apply.
|
Loading…
Reference in New Issue
Block a user