swiftwasm / swift

WebAssembly support for the Swift programming language

Home Page:https://swiftwasm.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Seems async `Task` doesn't work

MihaelIsaev opened this issue Β· comments

Description

I'm trying to execute async code wrapped into Task but it never called.

Steps to reproduce

  1. Create new carton project using carton init
  2. Use the following example
@main
public struct CartonApp {
    public private(set) var text = "Hello, World!"

    public static func main() {
        print(CartonApp().text)
        Task {
            print("async Task works") // this never calls
        }
    }
}

Expected behavior

async Task works should be printed into console

Environment

  • Swift compiler version info
SwiftWasm Swift version 5.7.1 (swiftlang-5.7.1)
Target: arm64-apple-darwin21.6.0
  • Xcode version info
Xcode 14.1
Build version 14B47b
  • Deployment target:
WASI

@kateinoigakukun

I just tried to use JSClosure.async which doesn't work.
Code inside of Task inside of synchronous JSClosure doesn't work also. (it is same as JSClosure.async)
Then I tried to create new carton project just to check if it works there, but unfortunately it doesn't.

The only place where Task works is

static func main() async {
    Task {
        print("πŸ”†πŸ”†πŸ”†πŸ”†πŸ”†πŸ”†") // works
    }
}

But it never works anywhere later.

And btw it throws this in the WebInspector console
Screenshot 2023-04-08 at 19 53 04

Not sure if I'm missing something.
@kateinoigakukun please help πŸ™

One more example

import JavaScriptKit

@main
public struct CartonApp {
    public private(set) var text = "Hello, World!"

    public static func main() async {
        print(CartonApp().text)
        if let docObj = JSObject.global.document.object, let bodyObj = JSObject.global.document.jsValue.body.object {
            var button = JSObject.global.document.createElement.function?.callAsFunction(this: docObj, "button").jsValue
            button?.innerText = "Click me".jsValue
            JSObject.global.document.jsValue.body.appendChild.function?.callAsFunction(this: bodyObj, button)
            if let buttonObj = button?.object {
                button?.addEventListener.function?.callAsFunction(this: buttonObj, "click", JSClosure({ args in
                    print("clicked") // works
                    Task {
                        print("clicked inside of Task") // never called
                    }
                    return .undefined
                }))
            }
        }
    }
}

JSClosure.async doesn't work at all

button?.addEventListener.function?.callAsFunction(this: buttonObj, "click", JSClosure.async({ args in
    print("clicked async") // never called
    return .undefined
}))

Thanks for reporting. This code however won't run as you expect, even on macOS:

@main
public struct CartonApp {
    public private(set) var text = "Hello, World!"

    public static func main() {
        print(CartonApp().text)
        Task {
            print("async Task works") // this never calls
        }
    }
}

Expected behavior

async Task works should be printed into console

It's a misunderstanding of the main function's lifetime. Once you start the task, the synchronous context reaches its end and there's nothing telling it to keep alive.
On other platform you can use CFRunLoopRun() to signal 'keep my process alive', but that's not supported in SwiftWasm.
The correct way to achieve that for the main function would be changing the signature from public static func main() to public static func main() async, remove the task and directly use async code. This is supported in latest SwiftWasm toolchains.

@yonihemi Thank you for the reply. Could you please help with JSClosure.async ? It doesn't work.

import JavaScriptKit

@main
public struct CartonApp {
    public private(set) var text = "Hello, World!"

    public static func main() async {
        print(CartonApp().text)
        if let docObj = JSObject.global.document.object, let bodyObj = JSObject.global.document.jsValue.body.object {
            var button = JSObject.global.document.createElement.function?.callAsFunction(this: docObj, "button").jsValue
            button?.innerText = "Click me".jsValue
            JSObject.global.document.jsValue.body.appendChild.function?.callAsFunction(this: bodyObj, button)
            if let buttonObj = button?.object {
                button?.addEventListener.function?.callAsFunction(this: buttonObj, "click", JSClosure.async({ args in
                    print("clicked async") // never called
                    return .undefined
                }))
            }
        }
    }
}

I don't see calls to JavaScriptEventLoop.installGlobalExecutor() in your sample code, per JSKit documentation in its README.md.

That's awesome, that's exactly what I missed!πŸ”₯ Thank you very much for pointing me @MaxDesiatov I appreciate it!