Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose QPromise to QML #5

Open
Adphi opened this issue Feb 15, 2018 · 10 comments
Open

Expose QPromise to QML #5

Adphi opened this issue Feb 15, 2018 · 10 comments

Comments

@Adphi
Copy link

Adphi commented Feb 15, 2018

Hi !
First: thanks for your beautiful work on this.
Is there any plan to support QPromise exposition to QML ?

@simonbrunel
Copy link
Owner

@Adphi Thank you :)

What do you mean exactly by "... support QPromise exposition to QML"?

I started writing a QML wrapper allowing to use the same functionalities in JavaScript:

var promise = new Promise(function(resolve) {
  //...
  resolve(42);
});

promise
  .then(function(res) { /*...*/ })
  .fail(function(err) { /*...*/ })
  .finally(function() { /*...*/ })
  // ...

Internally, it relies on a QPromise<QJSValue>. You can find that early implementation in the qtqmlpromise branch but it's definitely not ready for production, not tested and the public API may change. Also, it doesn't allow exchanging promises between both worlds (e.g. returning a QPromise<int> to QML), I still need to figure out what would be the best approach.

@Adphi
Copy link
Author

Adphi commented Feb 15, 2018

What do you mean exactly by "... support QPromise exposition to QML"?

I mean: exchanging promises between C++ and QML.
I was hopping to be able to return a Promise to QML from C++.
For example:

class CustomObjectWithPromise : public QObject
{
    Q_OBJECT
public:
    explicit CustomObjectWithPromise(QObject *parent = nullptr);
    Q_INVOKABLE QtPromise::QPromise<int>* getIntAsync();

signals:

public slots:

private:
    QNetworkAccessManager *mManager;
};

@simonbrunel
Copy link
Owner

QPromise<T> is not a Q_OBJECT (or Q_GADGET) and its methods are not compatible with JavaScript (e.g. QPromise<T>::then() doesn't work with a JS function, ie. QJSValue). Exception handling is also different between JavaScript and C++ and I really want to keep the C++ implementation not aware of QML. That's why we need a Q_GADGET wrapper (QJSPromise) around QPromise to translate from and to JavaScript values.

Then, we need to figure out how to convert from/to QJSPromise (I was thinking of adding a QPromise<T>.as<U>() method?). Once everything implemented and if that works as expected, your snippet would look like:

class CustomObjectWithPromise : public QObject
{
  Q_OBJECT

public:
  Q_INVOKABLE QtPromise::QJSPromise getIntAsync();
  {
     // readIntValueAsync returning QPromise<int>
     return readIntValueAsync().as<QJSValue>();
  }
};

Note: QPromise has been designed to avoid dealing with pointers, you shouldn't return a QPromise<T>* but instead a QPromise<T>.

@Adphi
Copy link
Author

Adphi commented Feb 15, 2018

Thank you.
I'll keep an eye on this.

@pwuertz
Copy link
Contributor

pwuertz commented May 8, 2018

@simonbrunel What is the biggest issue to solve for getting QML Promise in a technology preview state? As in knowing full well that the API may change, but being confident that then/fail/finally do what you would expect? I've seen there are some automated tests in the wip branch covering the behavior within QML already. Wouldn't it be save to use within QML then?

Do you like the idea of having a generic QPromise<T>.as<U>() function? If not, one could also think of a template constructor QJSPromise(QPromise<T>&&) specifically taking care of JSValue conversion, right?

@simonbrunel
Copy link
Owner

The major issue is accessing the current QJSEngine to convert the QPromise<T> value into a QJSValue. That's fine when the promise comes from QML but exposing a Q_SLOT or Q_INVOKABLE returning a QJSPromise is quite wonky. In some cases we could use the deprecated QJSValue::engine() accessor, but it's not enough, for example to reject a promise with a JS exception where there is no associated QJSValue.

Currently, it should be relatively easy to release a QML only implementation where the QJSPromise would be private and always bound to a QJSEngine. It would not initially allow to exchange promises between QML and C++ but at least it would offer a nice QML promise implementation.

I already implemented QPromise<T>.as<U>() in addition to implicit cast, I still need to write tests but I terribly lack of time. Though, that will not be enough to convert QPromise<T> -> QJSPromise because we will not have access to the QJSEngine.

@simonbrunel
Copy link
Owner

WIP on the Qt side for a native JavaScript promise: https://codereview.qt-project.org/#/c/122066/

@pwuertz
Copy link
Contributor

pwuertz commented May 8, 2018

Interesting, sounds like they are almost at the point of merging ES6 Promises into their JS engine. Crazy thought: Could this be an opportunity to use that momentum and convince Lars Knoll to include your library to follow up on this development on the C++ side? As TP of some sort? The point being that if both worlds had a reference Promise implementation, the QML engine might as well just implement automatic conversion from/to those types instead of being forced to shoehorn everything through QObject/Gadget/QJSValue. Like they did with supporting QByteArray <-> ArrayBuffer at some point.

@pwuertz
Copy link
Contributor

pwuertz commented May 8, 2018

So since creating QJSValue from C++ is a pain due to the burden of acquiring a QJSEngine, couldn't the Promise be shipped via QVariant instead? With QJSPromise being a registered type, Qt should be converting from/to QVariant transparently..

@simonbrunel
Copy link
Owner

@pwuertz FYI, a C++ promise like API is under investigation for Qt 6 (QTBUG-80908).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants