Does not print("Finish") Await Task.sleep(nanoseconds:) on Command Line

If Task.sleep() is too long, it will fail.

  • Widnows 10
  • Docker
  • Swift
    • Swift version 5.5.1 (swift-5.5.1-RELEASE)
    • Target: x86_64-unknown-linux-gnu

Fail

Task {
    print("Start")

    do {
        try await Task.sleep(nanoseconds: 1_000_000_000)
    }
    catch {
        print("Error")
    }

    print("Finish")
}

// output
root@80270fb670dc:/usr/src# swift testSleep.swift
Start
root@80270fb670dc:/usr/src# 

Success

Task {
    print("Start")

    do {
        try await Task.sleep(nanoseconds: 1_000)
    }
    catch {
        print("Error")
    }

    print("Finish")
}
// output
root@80270fb670dc:/usr/src# swift testSleep.swift
Start
Finish
root@80270fb670dc:/usr/src# 

Is anything awaiting the completion of this task?

1 Like

nothing.
all code is posted code.

Nothing is waiting for that task to complete before finishing the program execution.
When creating a task with the Task.init or Task.detached the program doesn't magically wait for them to finish.
I recommend you to use a @main struct with an async run to get a proper async context on a CLI app.

// rename your file other than main.swift
@main
struct App {
    static func main() async throws {
        print("Start")

        do {
            try await Task.sleep(nanoseconds: 1_000_000_000)
        }
        catch {
            print("Error")
        }

        print("Finish")
    }
}

Output:

Start
Finish
Program ended with exit code: 0
4 Likes

can it run with swift main.swift?

I tried then error has occured.

Can I run main.swift without compile?

root@80270fb670dc:/usr/src# swift run main.swift
warning: 'swift run file.swift' command to interpret swift files is deprecated; use 'swift file.swift' instead
main.swift:1:1: error: 'main' attribute cannot be used in a module that contains top-level code
@main
^
main.swift:1:1: note: top-level code defined in this source file
@main
^
root@80270fb670dc:/usr/src# 

As the diagnostics tell you:

It is not allowed to designate a main entry point with @main while also having top-level code (which is a main entry point), since those would be mutually contradictory as to which is the actual main entry point. Further, any module that contains a file named main.swift is considered to have top-level code. Please direct your attention to @Alejandro_Martinez's comment:

@Alejandro_Martinez

Sorry I made a mistake in reading.

change file name to "testSleep.swift"

root@80270fb670dc:/usr/src# swift testSleep.swift
testSleep.swift:1:1: error: 'main' attribute cannot be used in a module that contains top-level code
@main
^
testSleep.swift:1:1: note: top-level code defined in this source file
@main
^
root@80270fb670dc:/usr/src# 

The diagnostic tells you exactly what's not right: you still have top-level code.

if I delete @main, swift testSleep.swift shows nothing.

// testSleep.swift
@main
struct App {
    static func main() async throws {
        print("Start")

        do {
            try await Task.sleep(nanoseconds: 1_000_000_000)
        }
        catch {
            print("Error")
        }

        print("Finish")
    }
}

Ah, I understand your question. You don't have any other code except what you're showing, and you're literally running swift testSleep.swift. Unfortunately, that is just not supported.

You'll need to create a new Swift package (swift package init --type executable) and put your code in the package (and not in a file named main.swift!). Then you can just invoke swift run and it will run your code. (On macOS, you will also need to set the supported platform by adding platforms: [.macOS(.v10_15)] to Package.swift.)

2 Likes

Or just run swiftc taskSleep.swift and then the resulting executable ./taskSleep.

2 Likes

Thanks for answering.

I found another method.
But Maybe this is not best.

Process doesn't finish when it shows "Finish".

Task {
    print("Start")

    do {
        try await Task.sleep(nanoseconds: 1_000_000_000)
    }
    catch {
        print("Error")
    }

    print("Finish")
}

RunLoop.main.run() // <-Add
swift testSleep.swift

You've already been given a working example. Use the @main version that was posted above. Put it in a file not named main.swift. (I'll assume testSleep.swift.) Compile it using swiftc testSleep.swift. Run it using ./testSleep.

1 Like

Thanks!
using compile result is perfect.

I want method without compile.
But swift/swiftc doesnt support running async code without compile.