Async/await in Playgrounds?

func sampleFunc() async {
    // body
}

@main
struct MainApp {
    static func main() async {
        await sampleFunc()
    }
}

The bare-minimum examples of initiating async calls apparently rely on wrapping it in something syncronous, but in Playrounds, using @Main errors that “attribute cannot be used in a module that contains top-level code” and offers a pointer to the sounce file defining that top-level code but tapping it only takes me to the top of my own playground with the same alert whereupon pressing the same pointer button takes me back to where I just tapped from.

Is it impossible to actually utilize async/await in Playgrounds due to limitations on how to invoke it properly by placing it within something syncronous? If there’s a solution, I’d love to learn it, but it’s rare to find anyone who bothers to mention the initialization issue to begin with in materials I’m finding out there, let alone any fodder for insight into the Playgrounds error (I’m brand-new to programming; Swift Playgrounds is my 1st formal try at learning how—I want to make the best possible solve for the final lesson of Learn to Code 2’s Parameters chapter, Twin Peaks).

1 Like

There are other ways to initiate async code from a synchronous context. You don't need to create an @main type. Just use Task:

import Foundation
import _Concurrency

func sampleFunc() async {
    print("sampleFunc")
    try? await Task.sleep(nanoseconds: 1_000_000_000)
}

Task {
    await sampleFunc()
    print("done")
}

4 Likes

This is helpful inthat I am able to see the 1st print output, thank you for that, however the 2nd print, for “done”, never shows up even though I’m not getting any errors🤔

P. S. I had taken several stabs at Task before my original post, but I didn’t import at the top, so another thanks, for including that in your example.

Add RunLoop.main.run() at the end.

1 Like

Huzzah! Thank you! That works😀

I just run you code in an iOS playground, "done" is printed after the playground reach the end. Why did @dynamist has his problem and not me?

Sadly that will keep the playground running until stop manually. Not a big deal but there should a way to wait for the task completion so when is done the playground can finish correctly.

Must have been some cruft from my session, because I quit and relaunched the app seeing your reply, and it does work without that final statement about main👌

Thanks so much, everyone!:pray:

In this example, the execution stops at the end:

import Foundation
import _Concurrency

func sampleFunc() async {
    print("sampleFunc")
    try? await Task.sleep(nanoseconds: 1_000_000_000)
}

Task {
    await sampleFunc()
    print("done")
    CFRunLoopStop(CFRunLoopGetMain())
}

CFRunLoopRun()
2 Likes

This is a helpful lead on other contexts I will probably come back to soon (I presume CF stands for Core Foundation):+1:

yes

1 Like

Another option is to use the built in PlaygroundSupport module that keeps the playground running until you specify otherwise:

import Foundation
import _Concurrency
import PlaygroundSupport

func sampleFunc() async {
    print("sampleFunc")
    try? await Task.sleep(nanoseconds: 1_000_000_000)
}

Task {
    await sampleFunc()
    print("done")
    PlaygroundPage.current.finishExecution()
}

PlaygroundPage.current.needsIndefiniteExecution = true
5 Likes

How would I get an async value to actually display on the right-hand side of the playground like everything else? I haven't been able to figure out how to get a value out of a Task back into synchronous context (printing doesn't count). Is that simply impossible?

NVM my bad. It actually does show the value on the righthand side from within a Task { ... }! I was just failing to compile altogether :sweat_smile: