I have the following method in one of my C++ classes (using QtWebEngine):
QString get()
{
QString result;
view->page()->runJavaScript("test();", [this](const QVariant &v)
{
result = v.toString();
});
return result;
}
It is to execute test()
JS function and return the result of this invocation.
Unfortunately, the callback is asynchronous and the program crashes. How can I make it work?
Dmitry's solution only works partially, unfortunately. In his example the code is invoked upon pressing a button. However, if the same code is moved to execute while the window is loading, the slot for the finished script (onScriptEnded) is never called. To fix this, I had to wrap the call to the evaluation of JS itself (called evaluateJavaScript in my project) in another QTimer::singleShot:
Unfortunately, in my real project I have many calls, often one evaluation waiting for another before it to finish. It's practically impossible to use this every single time so I still look for a solution.
The callback is asynchronous because the JavaScript execution occurs not only in another thread but in another process. So there is no way to make it fully synchronous.
The best possible solution would be to migrate your C++ code to work asynchronously. If you can't do that, the only feasible solution is to use
QEventLoop
, somewhat like this:However, note that this example is oversimplified for a real-world usage: you need to ensure the JavaScript was not ran before the event loop is started. The most proper way to do that would involve implementing a proper slot instead of a lambda + factoring out the call to
view->page()->runJavaScript()
into another slot which would be called asynchronously after starting the event loop. It is a lot of glue code for such a seemingly simple task but that's what it takes. Here's an example:MainWindow.h
MainWindow.cpp
C++ invokes javascript function
This is much straightforward compared with invoking C++ function from js. Just use runJavaScript passing the function as the parameter as follows:
It assumes the jsfun is already defined on your web page, otherwise, you have to define it in the string parameter. The return value is asynchronously passed to the lambda expression as the parameter v.