ceifa / wasmoon

A real lua 5.4 VM with JS bindings made with webassembly
MIT License
462 stars 27 forks source link

Cannot await inside callback #22

Closed timstableford closed 8 months ago

timstableford commented 3 years ago
local res = sleep(1):next(function ()
    sleep(10):await()
    return 15
end)
print("res", res:await())

Returns

Error: Lua Error(ErrorRun/2): cannot resume dead coroutine
    at Thread.assertOk (/home/tstableford/projects/wasmoon/dist/index.js:409:23)
    at Thread.<anonymous> (/home/tstableford/projects/wasmoon/dist/index.js:142:22)
    at Generator.throw (<anonymous>)
    at rejected (/home/tstableford/projects/wasmoon/dist/index.js:26:69)

This makes sense because aren't async and aren't threads. That means they attempt to run within the parent thread. If they did run in a child thread then it would be possible to make jsFunc's async but then it would get a bit mad with async everywhere.

Maybe there's a good solution and maybe not, if there's not though it should be documented.

ceifa commented 3 years ago

A quickly workaround for that:

function async(callback)
    return function(...)
        local co = coroutine.create(callback)
        local safe, result = coroutine.resume(co, ...)

        return Promise.create(function(resolve, reject)
            local function step()
                if coroutine.status(co) == "dead" then
                    local send = safe and resolve or reject
                    return send(result)
                end

                safe, result = coroutine.resume(co)

                if safe and result == Promise.resolve(result) then
                    result:finally(step)
                else
                    step()
                end
            end

            result:finally(step)
        end)
    end
end
ceifa commented 3 years ago

Packages cannot use top-await too, since required files run on the main thread

local file = io.open("test.lua", "w+")
file:write([[
    coroutine.yield("potato")
]])
file:flush()

local co = coroutine.create(function()
    coroutine.yield("hello")
    require("test")
    coroutine.yield("world")
end)

print(coroutine.resume(co)) -- hello
print(coroutine.resume(co)) -- attempt to yield across a C-call boundary
print(coroutine.resume(co)) -- dead coroutine
tims-bsquare commented 8 months ago

@ceifa I think you can close this now it's documented in the readme

codingMASTER398 commented 2 months ago

A quickly workaround for that:

function async(callback)
    return function(...)
        local co = coroutine.create(callback)
        local safe, result = coroutine.resume(co, ...)

        return Promise.create(function(resolve, reject)
            local function step()
                if coroutine.status(co) == "dead" then
                    local send = safe and resolve or reject
                    return send(result)
                end

                safe, result = coroutine.resume(co)

                if safe and result == Promise.resolve(result) then
                    result:finally(step)
                else
                    step()
                end
            end

            result:finally(step)
        end)
    end
end

How would you implement this? It's unclear. I assume that you can call it like

await lua.global.get("async")(function)(options)

and that works for the first co-routine tick, then errors with attempt to index a nil value (global 'Promise').

I'm trying to call a Lua function from JS, that inside awaits for a function to return in JS.

lontrr commented 3 weeks ago

A quickly workaround for that:

function async(callback)
    return function(...)
        local co = coroutine.create(callback)
        local safe, result = coroutine.resume(co, ...)

        return Promise.create(function(resolve, reject)
            local function step()
                if coroutine.status(co) == "dead" then
                    local send = safe and resolve or reject
                    return send(result)
                end

                safe, result = coroutine.resume(co)

                if safe and result == Promise.resolve(result) then
                    result:finally(step)
                else
                    step()
                end
            end

            result:finally(step)
        end)
    end
end

How would you implement this? It's unclear. I assume that you can call it like

await lua.global.get("async")(function)(options)

and that works for the first co-routine tick, then errors with attempt to index a nil value (global 'Promise').

I'm trying to call a Lua function from JS, that inside awaits for a function to return in JS.

I have the same question