qtpromise icon indicating copy to clipboard operation
qtpromise copied to clipboard

Expose QPromise to QML

Open Adphi opened this issue 7 years ago • 10 comments
trafficstars

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

Adphi avatar Feb 15 '18 11:02 Adphi

@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.

simonbrunel avatar Feb 15 '18 13:02 simonbrunel

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;
};

Adphi avatar Feb 15 '18 13:02 Adphi

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>.

simonbrunel avatar Feb 15 '18 13:02 simonbrunel

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

Adphi avatar Feb 15 '18 14:02 Adphi

@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?

pwuertz avatar May 08 '18 07:05 pwuertz

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 avatar May 08 '18 17:05 simonbrunel

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

simonbrunel avatar May 08 '18 18:05 simonbrunel

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 avatar May 08 '18 18:05 pwuertz

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..

pwuertz avatar May 08 '18 18:05 pwuertz

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

simonbrunel avatar Feb 06 '20 22:02 simonbrunel