QtPromise is the Qt/C++ implementation of the Promises/A+ specification. A translation of this specification is provided in the appendix.
QtPromise is based on Qt5.6 and above, including Qt6 of course.
github address: https://github.com/simonbrunel/qtpromise
Novice navigation: Getting Started | QtPromise
API manual: API Reference , each API provides examples.
Promises are a solution to asynchronous programming that can replace traditional solutions - callback functions and events.
Three characteristics of Promise:
☆The state of the object is not affected by the outside world.
☆Once the state changes, it will not change again, that is to say, Promise has only one state at any time.
☆Invoke asynchronously in a synchronous manner, and realize then chain calls by returning a new promise.
Three disadvantages of Promise:
☆ Promise cannot be cancelled. Once it is created, it will be executed immediately and cannot be canceled halfway.
☆If the callback function is not set, the error thrown inside the Promise will not be reflected to the outside.
☆When it is in the pending state, it is impossible to know which stage it has progressed to, whether it has just started or is about to be completed.
If family members doing Qt development have needs for asynchronous programming, they can try to use this open source library.
The method of use is very simple, the QtPromise source code only has header files, just add qtpromise.pri in the source code directory to the pro file of your own project.
include($$PWD/qtpromise/qtpromise.pri)
Then include the module's header file
#include <QtPromise>
a simple demo
#include <QCoreApplication>
#include <QtPromise>
#include <QNetworkAccessManager>
#include <QNetworkReply>
using namespace QtPromise;
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QNetworkAccessManager *manager = new QNetworkAccessManager();
QString url = "http://www.baidu.com";
// 360安全卫士用于测试超时.
// QString url = "http://dl.360safe.com/setup.exe";
// 可以断网验证网络错误.
QPromise<QByteArray>{[&](
const QtPromise::QPromiseResolve<QByteArray>& resolve,
const QtPromise::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();
});
}}
.timeout(5000)
.then([](const QByteArray &data){
// 如果5秒内下载成功,进行下一步处理.
qDebug()<<data<<"AAAAA";
})
.fail([](QNetworkReply::NetworkError error) {
// 网络错误.
qDebug()<<error<<"BBBBB";
})
.fail([](const QPromiseTimeoutException& error) {
// 超时报错.
qDebug()<<error.what()<<"CCCCC";
});
return a.exec();
}
It should be noted that the definition of then:
QPromise<T>::then(Function onFulfilled, Function onRejected)
QPromise<T>::then(Function onFulfilled)
onRejected is optional, and the reason for rejection at this time is the same as the QPromise that called it. Because of the same. It is recommended to use fail to handle the error. If you must use onRejected to handle it, you can change it to the following.
.then([](const QByteArray &data){
// 如果5秒内下载成功,进行下一步处理.
qDebug()<<data<<"AAAAA";
}, [](QNetworkReply::NetworkError error) {
// 网络错误.
qDebug()<<error<<"BBBBBBBB";
})
.fail([](const QPromiseTimeoutException& error) {
// 超时报错.
qDebug()<<error.what()<<"CCCCC";
});
appendix:
An open, robust, and universal JavaScript Promise standard. Developed by developers, for developers' reference.
Translation term
- Fulfill : refers to a series of operations performed when a promise succeeds, such as state changes and callback execution. Although it
fulfill
is used to , it is oftenresolve
referred to in future promise implementations. - Reject (reject) : Refers to a series of operations performed when a promise fails.
- Eventual value : The so-called eventual value refers to the value passed to the resolution callback when the promise is resolved . Since the promise has a one-time feature, when this value is passed, it marks the end of the promise waiting state, so Called the final value, sometimes simply referred to as the value (value).
- Reason : That is, the rejection reason, which refers to the value passed to the rejection callback when the promise is rejected .
Promise represents the final result of an asynchronous operation, and the main way to interact with it is then
the method, which registers two callback functions to receive the final value of the promise or the reason why the promise cannot be executed.
This specification lists then
the implementation process of the method in detail. All promises implemented in accordance with the Promises/A+ specification can use this standard as a reference basis to implement the then
method. Thus this specification is very stable. Although the Promise/A+ organization may revise this specification from time to time, it is mainly to deal with some special edge cases, and these changes are minor and backward compatible. If we are going to make a large-scale incompatible update, we will definitely consider it carefully, discuss it in detail, and test it rigorously in advance.
Historically speaking, this specification has actually made the recommendations in the previous Promise/A specification a standard of conduct: on the one hand, we have expanded the conventional behavior of the original specification, and on the other hand, we have deleted some special cases and useful part of the question.
Finally, the core Promises/A+ specification does not design how to create, resolve and reject promises, but instead focuses on providing a general approach then
. The above operation method for promises may be mentioned in other specifications in the future.
the term
Promise
promise is an then
object or function with methods whose behavior conforms to this specification;
thenable
It is an object or function that defines then
a method, which is translated as "has then
a method" in the text;
value
refers to any JavaScript legal value (including undefined
, thenable and promise);
exception
is throw
a value thrown by a using statement.
According to reason
Indicates the rejection reason for a promise.
Require
The state of the Promise
The current state of a Promise must be one of the following three states: Pending , Fulfilled, and Rejected .
Waiting state (Pending)
When in the waiting state, the promise needs to meet the following conditions:
- Can transition to execute state or reject state
Execution state (Fulfilled)
When in the execution state, the promise needs to meet the following conditions:
- Cannot move to any other state
- Must have an immutable future value
Rejected state (Rejected)
When in the rejected state, the promise needs to meet the following conditions:
- Cannot move to any other state
- must have an immutable reason
The immutability here refers to identity (that is, ===
the available judgment is equal), not to imply a deeper level of immutability ( Translator's Note: Cover means that when value or reason is not a basic value, only its reference address is required to be equal, but the attribute value can be changed).
then method
A promise must provide a then
method to access its current value, future value and reason.
The method of promise then
accepts two parameters:
promise.then(onFulfilled, onRejected)
parameter optional
onFulfilled
and onRejected
are optional parameters.
- If
onFulfilled
not a function, it must be ignored - If
onRejected
not a function, it must be ignored
onFulfilled
characteristic
If onFulfilled
it is a function:
- It must be called when
promise
the execution ends, and its first parameter ispromise
the final value of promise
It cannot be called until the end of execution- It cannot be called more than once
onRejected
characteristic
If onRejected
it is a function:
- It must be called when
promise
it is rejected, and its first parameter ispromise
the reason - It cannot be called until
promise
it is rejected - It cannot be called more than once
call timing
onFulfilled
and can onRejected
only be called if the execution environment stack contains only platform code Note 1
call request
onFulfilled
and onRejected
must be called as a function (i.e. no this
value) Note 2
called multiple times
then
promise
method can be called multiple times by the same
- When
promise
executed successfully, allonFulfilled
callbacks need to be called in order according to their registration order - When
promise
the execution is rejected, allonRejected
must be called back in sequence according to their registration order
return
then
The method must return an promise
object Note 3
promise2 = promise1.then(onFulfilled, onRejected);
- If
onFulfilled
eitheronRejected
returns a valuex
, run the following Promise resolution process :[[Resolve]](promise2, x)
- If
onFulfilled
oronRejected
throws an exceptione
,promise2
execution must be rejected, returning the rejection reasone
- If
onFulfilled
not a function andpromise1
executes successfully,promise2
must execute successfully and return the same value - If
onRejected
it is not a function andpromise1
refuses to execute,promise2
it must refuse to execute and return the same reason
Translator's Note: It is very important to understand the "return" part above, that is: whether promise1
it is rejected or resolved, promise2
it will be resolved, and it will be rejected only when an exception occurs .
Promise resolution process
Promise resolution is an abstract operation that takes an input promise
and a value, we say that [[Resolve]](promise, x)
if x
there is then
a method and it looks like a Promise, the resolver tries to promise
accept x
the state; otherwise it x
executes with the value promise
.
This thenable feature makes the implementation of Promise more versatile: as long as it exposes a then
method that conforms to the Promise/A+ protocol; it also enables implementations that conform to the Promise/A+ specification to be compatible with those less standardized but usable implementations can coexist well.
To run [[Resolve]](promise, x)
, follow these steps:
x
promise
equal to
If promise
and x
point to the same object, execute TypeError
with reasonpromise
x
for Promise
If x
Promise, make the promise
accepted x
state Note 4 :
- If
x
in the waiting state,promise
it needs to remain in the waiting state untilx
it is executed or rejected - If
x
in execution state, execute with the same valuepromise
- If
x
in denial state, deny with the same reasonpromise
x
as an object or function
If x
it is an object or function:
- Assign
x.then
the value tothen
note 5 - If
x.then
an error is thrown when fetching the valuee
, ite
will be rejected with reasonpromise
- If
then
it is a function, it will be invokedx
as the scope of the function .this
Pass two callback functions as parameters, the first parameter is calledresolvePromise
and the second parameter is calledrejectPromise
:- If called
resolvePromise
with ay
value argument, runs[[Resolve]](promise, y)
- Reject
rejectPromise
withr
reason if called with reason parameterr
promise
- If
resolvePromise
bothrejectPromise
are called, or are called multiple times with the same parameter, the first call takes precedence and the remaining calls are ignored - If the calling
then
method throws an exceptione
:- Ignored if
resolvePromise
or already calledrejectPromise
e
Otherwise, grounds for refusalpromise
- Ignored if
- If
then
it is not a function, it is executedx
as an argumentpromise
- If called
- If
x
it is not an object or a function, it is executedx
as a parameterpromise
If a promise is resolved by an object in a circular thenable[[Resolve]](promise, thenable)
chain whose recursive nature causes it to be called again, the algorithm above will fall into an infinite recursion. Algorithms are not mandatory, but donors are encouraged to detect whether such recursion exists, and if detected, reject it with an identifiableTypeError
justification6 .promise
note
-
Note 1 The platform code here refers to the implementation code of the engine, environment and promise. In practice, it should be executed asynchronously
onFulfilled
withonRejected
the method, and should bethen
executed in the new execution stack after the round of event loop where the method is called. This event queue can be implemented using a "macro-task" mechanism or a "micro-task" mechanism. Since the promise implementation code itself is platform code ( Translator's Note: that is, it is all JavaScript), the code itself may already contain a task scheduling queue or "springboard" when processing the program ).Translator's Note: The two concepts of macrotask and microtask are mentioned here, which represent two classifications of asynchronous tasks. When suspending tasks, the JS engine will divide all tasks into these two queues according to their categories. First, take out the first task from the macrotask queue (this queue is also called task queue), and take it out of the microtask queue after execution All the tasks in the queue are executed sequentially; after that, the macrotask tasks are fetched, and the cycle repeats until the tasks in the two queues are all fetched.
The specific classification of the two categories is as follows:
- macro-task: script (overall code),
setTimeout
,setInterval
,setImmediate
, I/O, UI rendering -
micro-task:
process.nextTick
,Promises
(here refers to the native Promise implemented by the browser),Object.observe
,MutationObserver
- macro-task: script (overall code),
-
Note 2 means that in strict mode (strict) ,
this
the value of the function isundefined
; in non-strict mode, it is a global object. -
NOTE 3 Code implementations are permitted if all requirements are met
promise2 === promise1
. Each implementation is required to document whether and under what conditions it allows itpromise2 === promise1
. -
Note 4 In general,
x
we only consider a promise to be a true promise if it conforms to the current implementation . This rule allows those special case implementations to accept the state of Promises that meet known requirements. -
Note 5 In this step, we first store a pointed
x.then
reference, and then test and call the reference to avoid multiple accesses tox.then
properties. This precaution ensures the consistency of this property, since its value may be changed across retrieval calls. -
Note 6 Implementations should not place a limit on the depth of thenable chains, and assume that recursion beyond this limit is an infinite loop. Only true cyclic recursion should cause
TypeError
an exception; if an infinite chain of thenables is not the same, recursion is always the correct behavior.
Translation source: http://malcolmyu.github.io/malnote/2015/06/12/Promises-A-Plus/
Original link: https://blog.csdn.net/caoshangpa/article/details/129696818