Non-blocking QML
-
Situation
You either have a QObject derivative that exposes a Q_INVOKABLE method to QML, say myapi.myfunc.. or you have a pure JavaScript function myjsfunc defined in the QML code. In both you do some heavy calculations and/or (network) I/O.Problem
First: both myapi.myfunc and myjsfunc are asynchronous and needs proper async handling
Second: You don't want neither myapi.myfunc nor myjsfunc to block the event loop while it's executingPossible Solution
Return a Promise object! and let the user define the handling on it. In QML/JavaScript you can then define some asynchronous logic this way:import "myfsfunc.js" as JsBusinessLogic SomeItem { onSomething: { myapi.myfunc(...) .then( function(firstResult) { //success! make another async call return JsBusinessLogic.myjsfunc(firstResult) }, function(err, msg) { //failure! myfunc returned an error } ) .then( function(secondResult) { //success! second call succeeded, EXIT }, function(err, msg) { //failure! second call returned an error } ) .catch(function() { //something went seriously wrong }) } }
Meanwhile myjsfunc could look somewhat like this:
function myjsfunc() { return new Promise(function(resolve, reject) { //do some heavy lifting... if(result == good) { resolve(result) return } reject("baaaad results, error!") }) }
The promise object allows easy chaining of async operations and proper error and exception handling, but it still wont't change the fact that it's executed by the event loop and will inevitably block it, which ofcourse is unacceptable.
Question
So.... to make the async Promise non-blocking, I'd need to implement the Promise class in a way that uses a thread pool to execute the Promises in the background?
This way when you call an invokable C++ method or a JS function both create and return a Promise object and exit! not blocking the loop any longer!, right?!
The promise itself is then executed in a separate thread from the thread pool in the background. When it's finished its calculation and/or waiting for external resources it will run the JS handlers defined by then() or catch() in the event loop again, which won't block much.So did I correctly understand it? Would such kind of a Promise implementation work as described? Or is there probably an easier way?
Thanks!
P.S.
You might ask me why I'm implementing parts of the business logic in pure JavaScript when JS is not meant to be used for this in Qt? Well... you see.. executing 3rd party C++ is not an option if you want to build a safe OS basis, that's why JavaScript does the logic, JavaScript is easily sandboxed by the QQmlEngine while C++ is extraordinary hard to sandbox. Also using JavaScript for logic like this is what it was eventually designed for.. it's a scripting language and I use it to glue low-level modules and APIs together, just in an asynchronous, non-blocking way. -
This is the kind of things you become Qt Champion for: https://github.com/benlau/quickpromise
P.S.
Big up to the way you ask questions! -
This post is deleted!
-
After a detailed research of various asynchronous techniques such as Promises, Futures, Tasks and Reactive Extensions we came with a new paradigm that solved all our asynchronous problems very elegantly: The Streams Paradigm
It's inspired by the reactive approach, though superior in many aspects such as abstraction, abortion, resumables etc.
It's a completely rethought way of writing declarative code, making complex asynchronous operations and transactions (and probably even concurrency) a piece of cake! Nearly everything can be represented, thus abstracted away by a stream..
- UI Elements
- Sockets
- Requests (HTTP etc.)
- Calculations
- the list goes on...
This way streams become a consistent protocol of asynchronous and concurrent communication between various application components ranging from the UI frontend to the networked backend.
We published the first beta release of a QML implementation with a working example aboard, be sure to check it out!