diff --git a/.gitignore b/.gitignore
index dd3d84a..c360d95 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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
diff --git a/.travis.yml b/.travis.yml
index eff7ef1..f9d4c7d 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -30,3 +30,7 @@ script:
after_success:
- bash <(curl -s https://codecov.io/bash) -f coverage.info
+
+
+# gitbook install .
+# gitbook build . dist/docs
diff --git a/README.md b/README.md
index ce7efea..476ff50 100644
--- a/README.md
+++ b/README.md
@@ -1,433 +1,18 @@
-
+
# 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
-```
-
-### 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 download(const QUrl& url)
-{
- return QPromise([&](
- const QPromiseResolve& resolve,
- const QPromiseReject& 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 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.
-
-### Convert
-Converting `QFuture` to `QPromise` is done using the [`qPromise`](#helpers-qpromise) helper:
-
-```cpp
-QFuture future = QtConcurrent::run([]() {
- // {...}
- return 42;
-});
-
-QPromise promise = qPromise(future);
-```
-
-or simply:
-
-```cpp
-auto promise = qPromise(QtConcurrent::run([]() {
- // {...}
-}));
-```
-
-### Chain
-Returning a `QFuture` in [`then`](#qpromise-then) or [`fail`](#qpromise-fail) automatically translate to `QPromise`:
-
-```cpp
-QPromise input = ...
-auto output = input.then([](int res) {
- return QtConcurrent::run([]() {
- // {...}
- return QString("42");
- });
-});
-
-// output type: QPromise
-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 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
-### `QPromise::QPromise(resolver)`
-Creates a new promise that will be fulfilled or rejected by the given `resolver` lambda:
-
-```cpp
-QPromise promise([](const QPromiseResolve& resolve, const QPromiseReject& reject) {
- async_method([=](bool success, int result) {
- if (success) {
- resolve(result);
- } else {
- reject(customException());
- }
- });
-});
-```
-
-> **Note:** `QPromise` is specialized to not contain any value, meaning that the `resolve` callback takes no argument.
-
-**C++14**
-
-```cpp
-QPromise promise([](const auto& resolve, const auto& reject) {
- // {...}
-});
-```
-
-### `QPromise::then(onFulfilled, onRejected) -> QPromise`
-See [Promises/A+ `.then`](https://promisesaplus.com/#the-then-method) for details.
-
-```cpp
-QPromise 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::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 `` of the `output` promise depends on the return type of the `onFulfilled` handler:
-
-```cpp
-QPromise input = {...}
-auto output = input.then([](int res) {
- return QString::number(res); // -> QPromise
-});
-
-// output type: QPromise
-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 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`:
-
-```cpp
-QPromise input = ...
-auto output = input.then([](int res) {
- // {...}
-});
-
-// output type: QPromise
-output.then([]() {
- // `QPromise` `onFulfilled` handler has no argument
-});
-```
-
-You can also decide to skip the promise result by omitting the handler argument:
-
-```cpp
-QPromise 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 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.
-
-### `QPromise::fail(onRejected) -> QPromise`
-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
-});
-```
-
-### `QPromise::finally(handler) -> QPromise`
-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.
-
-### `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
-QPromise 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.
-
-### `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:
-
-```cpp
-int result = -1;
-QPromise 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::isPending() -> bool`
-Returns `true` if the promise is pending (not fulfilled or rejected), otherwise returns `false`.
-
-### `QPromise::isFulfilled() -> bool`
-Returns `true` if the promise is fulfilled, otherwise returns `false`.
-
-### `QPromise::isRejected() -> bool`
-Returns `true` if the promise is rejected, otherwise returns `false`.
-
-## QPromise (statics)
-### `[static] QPromise::resolve(value) -> QPromise`
-Creates a `QPromise` that is fulfilled with the given `value` of type `T`:
-
-```cpp
-QPromise compute(const QString& type)
-{
- if (type == "magic") {
- return QPromise::resolve(42);
- }
-
- return QPromise([](const QPromiseResolve& resolve) {
- // {...}
- });
-}
-```
-
-See also: [`qPromise`](#helpers-qpromise)
-
-### `[static] QPromise::reject(reason) -> QPromise`
-Creates a `QPromise` that is rejected with the given `reason` of *whatever type*:
-
-```cpp
-QPromise compute(const QString& type)
-{
- if (type == "foobar") {
- return QPromise::reject(QString("Unknown type: %1").arg(type));
- }
-
- return QPromise([](const QPromiseResolve& resolve) {
- // {...}
- });
-}
-```
-
-### `[static] QPromise::all(Sequence>) -> QPromise>`
-Returns a `QPromise>` 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 > promises{
- download(QUrl("http://a...")),
- download(QUrl("http://b...")),
- download(QUrl("http://c..."))
-};
-
-auto output = QPromise::all(promises);
-
-// output type: QPromise>
-output.then([](const QVector& res) {
- // {...}
-});
-```
-
-See also: [`qPromiseAll`](#helpers-qpromiseall)
-
-## Helpers
-### `qPromise(T value) -> QPromise`
-Similar to the `QPromise::resolve` static method, creates a promise resolved from a given `value` without the extra typing:
-
-```cpp
-auto promise = qPromise(); // QPromise
-auto promise = qPromise(42); // QPromise
-auto promise = qPromise(QString("foo")); // QPromise
-```
-
-This method also allows to convert `QFuture` to `QPromise` delayed until the `QFuture` is finished ([read more](#qtconcurrent-convert)).
-
-### `qPromiseAll(Sequence promises) -> QPromise>`
-This method simply calls the appropriated [`QPromise::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 > promises{...}
-
-auto output = qPromiseAll(promises);
-// eq. QPromise::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).
diff --git a/book.json b/book.json
new file mode 100644
index 0000000..6687058
--- /dev/null
+++ b/book.json
@@ -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"
+ }
+ }
+ }
+}
diff --git a/docs/README.md b/docs/README.md
new file mode 100644
index 0000000..4ca6ccb
--- /dev/null
+++ b/docs/README.md
@@ -0,0 +1,15 @@
+
+
+# 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).
diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md
new file mode 100644
index 0000000..f31184b
--- /dev/null
+++ b/docs/SUMMARY.md
@@ -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)
diff --git a/docs/assets/style.css b/docs/assets/style.css
new file mode 100644
index 0000000..acdd3e5
--- /dev/null
+++ b/docs/assets/style.css
@@ -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;
+}
diff --git a/docs/qtpromise/api-reference.md b/docs/qtpromise/api-reference.md
new file mode 100644
index 0000000..6bb5c44
--- /dev/null
+++ b/docs/qtpromise/api-reference.md
@@ -0,0 +1,25 @@
+## QPromise
+
+### Public Members
+
+* [`QPromise::QPromise`](qpromise/constructor.md)
+* [`QPromise::delay`](qpromise/delay.md)
+* [`QPromise::fail`](qpromise/fail.md)
+* [`QPromise::finally`](qpromise/finally.md)
+* [`QPromise::isFulfilled`](qpromise/isfulfilled.md)
+* [`QPromise::isPending`](qpromise/ispending.md)
+* [`QPromise::isRejected`](qpromise/isrejected.md)
+* [`QPromise::tap`](qpromise/tap.md)
+* [`QPromise::then`](qpromise/then.md)
+* [`QPromise::wait`](qpromise/wait.md)
+
+### Public Static Members
+
+* [`[static] QPromise::all`](qpromise/all.md)
+* [`[static] QPromise::reject`](qpromise/reject.md)
+* [`[static] QPromise::resolve`](qpromise/resolve.md)
+
+## Helpers
+
+* [`qPromise`](helpers/qpromise.md)
+* [`qPromiseAll`](helpers/qpromiseall.md)
diff --git a/docs/qtpromise/getting-started.md b/docs/qtpromise/getting-started.md
new file mode 100644
index 0000000..36548bc
--- /dev/null
+++ b/docs/qtpromise/getting-started.md
@@ -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
+```
+
+## 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 download(const QUrl& url)
+{
+ return QPromise([&](
+ const QPromiseResolve& resolve,
+ const QPromiseReject& 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 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
+});
+```
diff --git a/docs/qtpromise/helpers/qpromise.md b/docs/qtpromise/helpers/qpromise.md
new file mode 100644
index 0000000..56c3996
--- /dev/null
+++ b/docs/qtpromise/helpers/qpromise.md
@@ -0,0 +1,15 @@
+## `qPromise`
+
+```
+qPromise(T value) -> QPromise
+```
+
+Similar to the [`QPromise::resolve`](../qpromise/resolve.md) static method, creates a promise resolved from a given `value` without the extra typing:
+
+```cpp
+auto promise = qPromise(); // QPromise
+auto promise = qPromise(42); // QPromise
+auto promise = qPromise(QString("foo")); // QPromise
+```
+
+This method also allows to convert `QFuture` to `QPromise` delayed until the `QFuture` is finished ([read more](../qtconcurrent.md#convert)).
diff --git a/docs/qtpromise/helpers/qpromiseall.md b/docs/qtpromise/helpers/qpromiseall.md
new file mode 100644
index 0000000..9d9741f
--- /dev/null
+++ b/docs/qtpromise/helpers/qpromiseall.md
@@ -0,0 +1,16 @@
+## `qPromiseAll`
+
+```
+qPromiseAll(Sequence> promises) -> QPromise>
+qPromiseAll(Sequence> promises) -> QPromise
+```
+
+This method simply calls the appropriated [`QPromise::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 > promises{...}
+
+auto output = qPromiseAll(promises);
+// eq. QPromise::all(promises)
+```
+
diff --git a/docs/qtpromise/qpromise/all.md b/docs/qtpromise/qpromise/all.md
new file mode 100644
index 0000000..88b89c2
--- /dev/null
+++ b/docs/qtpromise/qpromise/all.md
@@ -0,0 +1,26 @@
+## `[static] QPromise::all`
+
+```
+[static] QPromise::all(Sequence> promises) -> QPromise>
+```
+
+Returns a `QPromise>` 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 > promises{
+ download(QUrl("http://a...")),
+ download(QUrl("http://b...")),
+ download(QUrl("http://c..."))
+};
+
+auto output = QPromise::all(promises);
+
+// output type: QPromise>
+output.then([](const QVector& res) {
+ // {...}
+});
+```
+
+See also: [`qPromiseAll`](../helpers/qpromiseall.md)
diff --git a/docs/qtpromise/qpromise/constructor.md b/docs/qtpromise/qpromise/constructor.md
new file mode 100644
index 0000000..302ed20
--- /dev/null
+++ b/docs/qtpromise/qpromise/constructor.md
@@ -0,0 +1,29 @@
+## `QPromise::QPromise`
+
+```
+QPromise::QPromise(Function resolver)
+```
+
+Creates a new promise that will be fulfilled or rejected by the given `resolver` lambda:
+
+```cpp
+QPromise promise([](const QPromiseResolve& resolve, const QPromiseReject& reject) {
+ async_method([=](bool success, int result) {
+ if (success) {
+ resolve(result);
+ } else {
+ reject(customException());
+ }
+ });
+});
+```
+
+> **Note:** `QPromise` is specialized to not contain any value, meaning that the `resolve` callback takes no argument.
+
+**C++14**
+
+```cpp
+QPromise promise([](const auto& resolve, const auto& reject) {
+ // {...}
+});
+```
diff --git a/docs/qtpromise/qpromise/delay.md b/docs/qtpromise/qpromise/delay.md
new file mode 100644
index 0000000..21af63d
--- /dev/null
+++ b/docs/qtpromise/qpromise/delay.md
@@ -0,0 +1,14 @@
+## `QPromise::delay`
+
+```
+QPromise::delay(int msec) -> 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
+});
+```
diff --git a/docs/qtpromise/qpromise/fail.md b/docs/qtpromise/qpromise/fail.md
new file mode 100644
index 0000000..5b6d8ec
--- /dev/null
+++ b/docs/qtpromise/qpromise/fail.md
@@ -0,0 +1,19 @@
+## `QPromise::fail`
+
+```
+QPromise::fail(Function onRejected) -> QPromise
+```
+
+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
+});
+```
diff --git a/docs/qtpromise/qpromise/finally.md b/docs/qtpromise/qpromise/finally.md
new file mode 100644
index 0000000..671c957
--- /dev/null
+++ b/docs/qtpromise/qpromise/finally.md
@@ -0,0 +1,15 @@
+## `QPromise::finally`
+
+```
+QPromise::finally(Function handler) -> QPromise
+```
+
+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.
diff --git a/docs/qtpromise/qpromise/isfulfilled.md b/docs/qtpromise/qpromise/isfulfilled.md
new file mode 100644
index 0000000..8b25e00
--- /dev/null
+++ b/docs/qtpromise/qpromise/isfulfilled.md
@@ -0,0 +1,7 @@
+# `QPromise::isFulfilled`
+
+```
+QPromise::isFulfilled() -> bool
+```
+
+Returns `true` if the promise is fulfilled, otherwise returns `false`.
diff --git a/docs/qtpromise/qpromise/ispending.md b/docs/qtpromise/qpromise/ispending.md
new file mode 100644
index 0000000..4fb5c15
--- /dev/null
+++ b/docs/qtpromise/qpromise/ispending.md
@@ -0,0 +1,7 @@
+# `QPromise::isPending`
+
+```
+QPromise::isPending() -> bool
+```
+
+Returns `true` if the promise is pending (not fulfilled or rejected), otherwise returns `false`.
diff --git a/docs/qtpromise/qpromise/isrejected.md b/docs/qtpromise/qpromise/isrejected.md
new file mode 100644
index 0000000..e982e51
--- /dev/null
+++ b/docs/qtpromise/qpromise/isrejected.md
@@ -0,0 +1,8 @@
+# `QPromise::isRejected`
+
+```
+QPromise::isRejected() -> bool
+```
+
+Returns `true` if the promise is rejected, otherwise returns `false`.
+
diff --git a/docs/qtpromise/qpromise/reject.md b/docs/qtpromise/qpromise/reject.md
new file mode 100644
index 0000000..3eb0e8f
--- /dev/null
+++ b/docs/qtpromise/qpromise/reject.md
@@ -0,0 +1,20 @@
+## `[static] QPromise::reject`
+
+```
+[static] QPromise::reject(any reason) -> QPromise
+```
+
+Creates a `QPromise` that is rejected with the given `reason` of *whatever type*:
+
+```cpp
+QPromise compute(const QString& type)
+{
+ if (type == "foobar") {
+ return QPromise::reject(QString("Unknown type: %1").arg(type));
+ }
+
+ return QPromise([](const QPromiseResolve& resolve) {
+ // {...}
+ });
+}
+```
diff --git a/docs/qtpromise/qpromise/resolve.md b/docs/qtpromise/qpromise/resolve.md
new file mode 100644
index 0000000..3d49f7f
--- /dev/null
+++ b/docs/qtpromise/qpromise/resolve.md
@@ -0,0 +1,22 @@
+## `[static] QPromise::resolve`
+
+```
+[static] QPromise::resolve(T value) -> QPromise
+```
+
+Creates a `QPromise` that is fulfilled with the given `value` of type `T`:
+
+```cpp
+QPromise compute(const QString& type)
+{
+ if (type == "magic") {
+ return QPromise::resolve(42);
+ }
+
+ return QPromise([](const QPromiseResolve& resolve) {
+ // {...}
+ });
+}
+```
+
+See also: [`qPromise`](../helpers/qpromise.md)
diff --git a/docs/qtpromise/qpromise/tap.md b/docs/qtpromise/qpromise/tap.md
new file mode 100644
index 0000000..fe93620
--- /dev/null
+++ b/docs/qtpromise/qpromise/tap.md
@@ -0,0 +1,18 @@
+## `QPromise::tap`
+
+```
+QPromise::tap(Function 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`](finally.md), this handler is **not** called for rejections.
+
+```cpp
+QPromise 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.
diff --git a/docs/qtpromise/qpromise/then.md b/docs/qtpromise/qpromise/then.md
new file mode 100644
index 0000000..be55e23
--- /dev/null
+++ b/docs/qtpromise/qpromise/then.md
@@ -0,0 +1,87 @@
+## `QPromise::then`
+
+```
+QPromise::then(Function onFulfilled, Function onRejected) -> QPromise
+QPromise::then(Function onFulfilled) -> QPromise
+```
+
+See [Promises/A+ `.then`](https://promisesaplus.com/#the-then-method) for details.
+
+```cpp
+QPromise 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::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 `` of the `output` promise depends on the return type of the `onFulfilled` handler:
+
+```cpp
+QPromise input = {...}
+auto output = input.then([](int res) {
+ return QString::number(res); // -> QPromise
+});
+
+// output type: QPromise
+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 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`:
+
+```cpp
+QPromise input = ...
+auto output = input.then([](int res) {
+ // {...}
+});
+
+// output type: QPromise
+output.then([]() {
+ // `QPromise` `onFulfilled` handler has no argument
+});
+```
+
+You can also decide to skip the promise result by omitting the handler argument:
+
+```cpp
+QPromise 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 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.
diff --git a/docs/qtpromise/qpromise/wait.md b/docs/qtpromise/qpromise/wait.md
new file mode 100644
index 0000000..e8795d0
--- /dev/null
+++ b/docs/qtpromise/qpromise/wait.md
@@ -0,0 +1,21 @@
+## `QPromise::wait`
+
+```
+QPromise::wait() -> QPromise
+```
+
+This method holds the execution of the remaining code **without** blocking the event loop of the current thread:
+
+```cpp
+int result = -1;
+QPromise 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
+```
diff --git a/docs/qtpromise/qtconcurrent.md b/docs/qtpromise/qtconcurrent.md
new file mode 100644
index 0000000..c894790
--- /dev/null
+++ b/docs/qtpromise/qtconcurrent.md
@@ -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.
+
+## Convert
+
+Converting `QFuture` to `QPromise` is done using the [`qPromise`](helpers/qpromise.md) helper:
+
+```cpp
+QFuture future = QtConcurrent::run([]() {
+ // {...}
+ return 42;
+});
+
+QPromise promise = qPromise(future);
+```
+
+or simply:
+
+```cpp
+auto promise = qPromise(QtConcurrent::run([]() {
+ // {...}
+}));
+```
+
+## Chain
+
+Returning a `QFuture` in [`then`](qpromise/then.md) or [`fail`](qpromise/fail.md) automatically translate to `QPromise`:
+
+```cpp
+QPromise input = ...
+auto output = input.then([](int res) {
+ return QtConcurrent::run([]() {
+ // {...}
+ return QString("42");
+ });
+});
+
+// output type: QPromise
+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 promise = ...
+promise.then([](int res) {
+ return QtConcurrent::run([]() {
+ // {...}
+
+ if (!success) {
+ throw CustomException();
+ }
+
+ return QString("42");
+ });
+}).fail(const CustomException& err) {
+ // {...}
+});
+```
diff --git a/docs/qtpromise/thread-safety.md b/docs/qtpromise/thread-safety.md
new file mode 100644
index 0000000..b4eee3c
--- /dev/null
+++ b/docs/qtpromise/thread-safety.md
@@ -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.