Qt Excellent Open Source Project Seventeen: QtPromise

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 often resolve 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 is  promise 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 is  promise 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

thenpromise method can be called multiple times by  the same 

  • When  promise executed successfully, all  onFulfilled callbacks need to be called in order according to their registration order
  • When  promise the execution is rejected, all  onRejected 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 either  onRejected returns a value  x , run the following  Promise resolution process :[[Resolve]](promise2, x)
  • If  onFulfilled or  onRejected throws an exception  e ,  promise2 execution must be rejected, returning the rejection reason e
  • If  onFulfilled not a function and  promise1 executes successfully,  promise2 must execute successfully and return the same value
  • If  onRejected it is not a function and  promise1 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:

xpromise 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 until  x it is executed or rejected
  • If  x in execution state, execute with the same value promise
  • If  x in denial state, deny with the same reason promise

x as an object or function

If  x it is an object or function:

  • Assign  x.then the value to  then note 5
  • If  x.then an error is thrown when fetching the value  e , it e will be  rejected with reasonpromise
  • If  then it is a function, it will   be invoked x as the scope of the function  . thisPass two callback functions as parameters, the first parameter is called  resolvePromise and the second parameter is called  rejectPromise:
    • If  called resolvePromise with a y value  argument, runs[[Resolve]](promise, y)
    •  Reject  rejectPromise with r reason  if  called with reason parameterrpromise
    • If  resolvePromise both  rejectPromise 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 exception  e:
      •  Ignored if  resolvePromise or  already calledrejectPromise
      • e Otherwise,   grounds for refusalpromise
    • If  then it is not a function, it is executed x as  an argumentpromise
  • If  x it is not an object or a function, it is executed x 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 with  onRejected the method, and should be  then 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),  setTimeoutsetIntervalsetImmediate, I/O, UI rendering
    • micro-task: process.nextTickPromises(here refers to the native Promise implemented by the browser),  Object.observeMutationObserver

  • Note 2  means that in  strict mode (strict)  ,  this the value of  the function is undefined ; 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 it  promise2 === 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 to  x.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

Guess you like

Origin blog.csdn.net/caoshangpa/article/details/129696818