If I compile and run it “as is” the line print("completed task work") is never reached. If I add await try Task.sleep(for: .seconds(1)) it prints the message. The message from the deinitializer is not printed either. Should it be like this? Or do I miss something essential?
Ah, OK I see. Nice, thanks! One more thing: How do I have to do this (asynchronous context) without an explicit main? With the added code it doesn’t compile without …main() async…Or is that really the only way in this case?
The problem here is simply that the OP’s start method launches a task and immediately returns, so there is a simple race between the task that start launched and the termination of the program. As you noted, one would simply await that task.
The documentation clearly denotes that this is a "snippet of code", not a complete program. It explains the Task type, but that does not mean it has to be written without any presupposed understanding of concurrency in general.
Right at the end of the first paragraph it says (emphasis mine)
However, if you discard the reference to a task, you give up the ability to wait for that task’s result or cancel the task.
And if one starts a program, then starts the task, but then immediately lets the program finish, this is exactly what happens.
Of course I agree it's difficult to bring every aspect into each part of any documentation, but considering this is explaining how Task specifically works, imo it's fine that provided code is not necessarily a self-contained program.
It might be a bit strong to call it a “bug” in the documentation.
The whole point of this section is to talk about the “Task closure lifetime”. So, sure, if you allow the app to terminate before the Task closure reaches the end of its lifetime (e.g., in a command line app or a Playground without needsIndefiniteExecution), any discussions about Task lifetimes are obviously moot.
But if you put this in an app in an AppKit/UIKit/SwiftUI app (or playground with indefinite execution; or add a Task.sleep; etc.), it works just as outlined in the documentation. I might even argue that anything talking about the lifespan of an asynchronous task closure naturally implies that you wouldn’t do this in an app that is allowed to terminate before the closure finishes.
So, I would not be inclined to call it a “bug”. At worst, perhaps it is a little unclear.
Hi, it's me the real OP. It's going a long way. Hard to decide who to reply to first and I can't update my initial post. So I'm answering myself in the hope that everyone feels addressed.
It's like this: The issue of concurrency is still relatively new and not entirely trivial or intuitive to understand.
Unfortunately, many attempts to explain this are somewhat vague. In my opinion, this is actually the case in all languages that have such features.
That's why I think it's a bit unfortunate when examples in official documentation are not immediately executable. In books, there is the famous "...left as an exercise for the reader." Even that is sometimes borderline.
Then there's the following phenomenon: out there in the wild, there are countless examples of Swift code that don't run - either because they are outdated or simply wrong. It already looks like everyone has a Swift blog.
All this together makes life more difficult for interested learners.
Anyway, I am of the opinion that examples in official documentation aka "The Book" etc. should be executable. Just as I am of the opinion that there are far too few examples. But that's another topic. And problematic because it involves a lot of work.