How to await function in js side
frank-zsy opened this issue · comments
Here is the question, I set a global function to Lua use lua_setglobal
which read args from stack and then call js function and return the result. But if the js function returns a Promise, I can not resolve it in Lua side, I really want to auto resolve js function call so Lua side won't need to concern such thing.
The code is
set = (funcName, func) => {
wrapped = () => {
args = [...]; // read args from stack
res = func.call(null, ...args); // res can be a promise, support await here?
pushStack(L, res);
return resNumber; // push the res to stack and return res number
};
lua.lua_pushjsfunction(L, wrapped);
lua.lua_setglobal(L, funcName);
};
set('add', (a, b) => a + b);
If the return value is not a Promise, then everything works fine here, but if the return value is a Promise, I can't resolve on Lua side and if I add await
before func.call
and make wrapped
an async function, then I receive error message even the injected function returns a number value.
Any thought on this?
I think the only way is to run Lua code in a Lua coroutine, and then use lua_yield
and lua_resume
in your Javascript implementation.
It's the only way I found to make asynchronous call from Lua (callback or promises, it doesn't change much, it's the same thing in the end).
Now I don't have PC at hand to show you some working code, but maybe you just need a point in the right direction
@lorenzos thank you very much, I will look into Lua coroutine and lua_yield
, lua_resume
, it looks good!
@lorenzos Thank you, I just figured out and I will record it here in case anyone else has the same question.
If you want to make async calls in js to be a sync function from Lua, you need to use coroutine and yield
from outside the Lua and resume
until the Promise return
, here is some code that would solve the problem.
set = (funcName, func) => {
wrapped = (L) => {
args = [...]; // read args from stack
res = func.call(null, ...args);
if (res instance of Promise && lua.lua_isyieldable(L)) { // if return a Promise and currently in a coroutine
// if not in a coroutine, yield will throw an error about 'yield outside coroutine'
Promise.resolve(res).then(r => {
if (r === undefined) lua.lua_resume(L, from, 0); // no return value
else {
pushStack(L, r)
lua.lua_resume(L, from, 1); // only one return value
}
});
return lua.lua_yield(L, 0); // yield from outside to pause Lua code
} else {
pushStack(L, res);
return resNumber; // push the res to stack and return res number
}
};
lua.lua_pushjsfunction(L, wrapped);
lua.lua_setglobal(L, funcName);
};
set('add', async (a, b) => {
await waitFor(10); // wait for 10ms, which makes this function async and return a promise, Lua call will pause here to wait for return
return a + b;
});
Then you can use add in Lua code like this
local co = coroutine.create(function() -- must wrapped in a coroutine or you need to start Lua script as coroutine from js
local r = add(1, 2) -- add will return after 10ms pause, and you can still get the result to r
print(r)
end)
coroutine.resume(co)
@daurnimator a higher level JS library wrapper to support this idiom could be nice...
@daurnimator a higher level JS library wrapper to support this idiom could be nice...
sounds like fengari-lua/fengari-interop#2
@daurnimator a higher level JS library wrapper to support this idiom could be nice...
sounds like fengari-lua/fengari-interop#2
Why I didn't find this repo sooner, I almost write the interop part from scratch myself