I want to make async call to C++ native function, but have some problems: when I call native function in async block (in Promise, exactly), it blocks UI thread and no async call is made. I want to pass callback to C++ function and call it. How can I pass function object?
class frame : public sciter::window {
public:
frame() : window(SW_TITLEBAR | SW_RESIZEABLE | SW_CONTROLS | SW_MAIN | SW_ENABLE_DEBUG) {}
void assertion(const std::string& val)
{
stream << std::chrono::duration_cast<std::chrono::seconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count()
<< ' '
<< val
<< '\n';
stream.flush();
}
void asyncFunction(::sciter::value callback)
{
std::thread{ [callback]()
{
std::this_thread::sleep_for(std::chrono::milliseconds(5000)); // if I use it in same thread, it blocks UI thread and no async call is made
// call callback function
} }.detach();
}
SOM_PASSPORT_BEGIN(frame)
SOM_FUNCS(
SOM_FUNC(assertion),
SOM_FUNC(asyncFunction)
)
SOM_PASSPORT_END
private:
std::ofstream stream{ "log.txt" };
};
In this implementation I use another thread to make a logic. But if I want to return values (or notify, that call has completed), I need fire event (what I don't want to do, because logic will be spread on whole code) or call some sort of callback. sciter::value
have methods is_function_object
and is_function
, so, probably, I have opportinuty to cast value to the C++ function object. But how can I do this?
<html>
<head>
<title>Test</title>
</head>
<body>
<button #test>Assert</button>
<script>
function sleep(time) {
return new Promise(resolve => {
Window.this.frame.asyncFunction(function (result)
{
Window.this.frame.assertion(result);
resolve(result);
}); // resolve(Window.this.frame.asyncFunction()) blocks code execution until asyncFunction returns
});
}
async function answer() {
Window.this.frame.assertion("sleep start");
await sleep(5000);
Window.this.frame.assertion("sleep end");
return 50;
}
document.$("button#test").onclick = function () {
var p = answer();
p.then(result => { Window.this.frame.assertion("Ended: " + result) });
Window.this.frame.assertion("go next");
}
</script>
</body>
</html>
Well, here are a few possibilities: we can use callback.call()
if a function or function object was passed or just return Promise like object. Examples:
void asyncFunction(::sciter::value callback)
{
std::thread{ [callback]()
{
std::this_thread::sleep_for(std::chrono::milliseconds(5000));
sciter::value result = sciter::value(42);
callback.call(result); // invoke the callback function.
} }.detach();
}
That's how to call callback function. But we can simply return Promise like object (defined here):
sciter::value nativeAsyncFunction(int milliseconds)
{
sciter::om::hasset<NativePromise> promise = new NativePromise();
std::thread([=]() {
std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
promise->resolve(42);
}).detach();
return sciter::value::wrap_asset(promise);
}
In this case we don't need to create additional functions like sleep
, we just call nativeAsyncFunction
in answer
and await its result.