v0.4 Release
AsyncFuture 0.4 Release Notes
New Features
1) Improved Compilation Error Message on invalid callback
The compilation error related to the template in C++ is known to be giant and misleading. It is prioritized to inform you where the error occurs in the template declaration, which is not written by you and you have no knowledge about the implementation.
AsyncFuture v0.4 inserted many static_asserts in the subscribe()/context() to capture error in the early stage. It could reduce the no. of error messages and tell users what is wrong actually in their code.
Error Messages:
- Observe a QFuture but your callback contains an input argument
- Callback function should not take more than 1 argument
- The callback function is not callable. The input argument doesn't match with the observing QFuture type
2) Future Object Tracking - The advanced usage with QtConcurrent::mapped
The deferred object is now supported to track the status of another future object. It will synchronize the progressValue
/ progressMinimium
/ progressMaximium
and status of the tracking object. (e.g started signal)
For example:
auto defer = AsyncFuture::deferred<QImage>();
QFuture<QImage> mapped = QtConcurrent::mapped(files, readImage);
defer.complete(mapped); // defer.future() will be a mirror of `mapped`. The `progressValue` will be changed and it will emit "started" signal via QFutureWatcher
A practical use-case
QFuture<QImage> readImagesFromFolder(const QString& folder) {
auto worker = [=]() {
// Read files from a directory in a thread
QStringList files = findImageFiles(folder);
// Concurrent image reader
return QtConcurrent::mapped(files, readImage);
};
auto future = QtConcurrent::run(worker); // The type of future is QFuture<QFuture<QImage>>
auto defer = AsyncFuture::deferred<QImage>();
// defer object track the input future. It will emit "started" and `progressValueChanged` according to the status of the future of "QtConcurrent::mapped"
defer.complete(future);
return defer.future();
}
In the example code above, the future returned by defer.future()
is supposed to be a mirror of the result of QtConcurrent::mapped
. However, the linkage is not estimated in the beginning until the worker functions start QtConcurrent::mapped
In case it needs to track the status of a future object but it won’t complete automatically. It may use track() function
3) Support mutable lambda function
In AsyncFuture 0.3, the following is invalid because the deferred object is marked as const
auto defer = AsyncFuture::deferred<void>();
QtConcurrent::run([=]() {
defer.complete(); // Compilation error
});
Mutable lambda function is supported in v4.0, so it could be solved by marking the lambda function as mutable:
auto defer = AsyncFuture::deferred<void>();
QtConcurrent::run([=]() mutable {
defer.complete();
});
New API
AsyncFuture::observe(QFuture<QFuture> future)
This function creates an Observable<T>
object which provides an interface for observing the input future. That is designed to handle the following use-case:
QFuture<QImage> readImagesFromFolder(const QString& folder) {
auto worker = [=]() {
// Read files from a directory in a thread
QStringList files = findImageFiles(folder);
// Concurrent image reader
return QtConcurrent::mapped(files, readImage);
};
auto future = QtConcurrent::run(worker); // The type of future is QFuture<QFuture<QImage>>
auto defer = AsyncFuture::deferred<QImage>();
// defer object track the input future. It will emit "started" and `progressValueChanged` according to the status of the future of "QtConcurrent::mapped"
defer.complete(future);
return defer.future();
}
See Observable<T>
AsyncFuture::observe(object, SIGNAL(signal))
This function creates an Observable<QVariant>
object which contains a future to represent the result of the signal. You could obtain the future by the future() method. And observe the result by subscribe() / context() methods. The result of the future is equal to the first parameter of the signal.
QFuture<QVariant> future = observe(timer, SIGNAL(timeout()).future();
See Observable<T>
Added since v0.4
Observable::onProgress(callback)
Listens the progressValueChanged
and progressRangeChanged
signal from the observed future then trigger the callback. The callback function may return nothing or a boolean value. If the boolean value is false, it will remove the listener such that the callback may not trigger anymore.
Example
QFuture<int> future = QtConcurrent::mapped(input, workerFunction);
AsyncFuture::observe(future).onProgress([=]() -> bool {
qDebug() << future.progressValue();
return true;
});
// or
AsyncFuture::observe(future).onProgress([=]() -> void {
qDebug() << future.progressValue();
});
Deferred::track(future)
Track the progress and synchronize the status of the target future object.
It will forward the signal of started
, resumed
, paused
. And synchonize the progressValue
, progressMinimum
and progressMaximum
value by listening the progressValueChanged
signal from target future object.
Remarks: It won't complete a future even the progressValue
has been reached the maximum value.
Deferred<T>::complete(QList<T>)
Complete the future object with a list of result. A user may obtain all the value by QFuture::results().
API Changes
Deferred::complete(QFuture<T>
target)
Complete the deferred according to the target future. It will also track the progressValue
and status.
AsyncFuture::combine()
Supported to combine deferred object
Observable<void> o;
auto all = combine() << o;