/*:#localized(key: "StructuredVsUnstructured")
 # Structured vs. unstructured

 Swift concurrency has two families of tasks, and telling them apart is the single
 most useful mental model in this chapter.

 A **structured task** is a child whose lifetime is *bound to a scope* in your
 code. It's created inside a parent, and the parent cannot return until every
 child has finished (or been cancelled). You get structured tasks from
 **`async let`** (which you'll meet below) and from **task groups** (a later
 chapter). The compiler enforces the nesting, so children can't outlive their
 parent: no leaks, no forgotten work.

 An **unstructured task** is one you create by hand with `Task { }` or
 `Task.detached { }`. It is *not* tied to the surrounding scope. It keeps running
 even after the function that made it returns, and **you** are responsible for
 it: holding its handle, awaiting its value, cancelling it when appropriate.

 Here's the structured form. `async let` starts a child task immediately and
 binds its eventual result to a name; you `await` that name later.
 */
//#-editable-code
import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

func steepTea() async -> String {
    try? await Task.sleep(for: .milliseconds(500))
    return "green tea"
}

func warmMilk() async -> String {
    try? await Task.sleep(for: .milliseconds(300))
    return "steamed milk"
}

Task {
    async let tea = steepTea()       // child task starts now
    async let milk = warmMilk()      // this one too, concurrently

    let tray = await [tea, milk]     // wait for both, together
    print("On the tray: \(tray)")
    PlaygroundPage.current.finishExecution()
}
//#-end-editable-code
/*:#localized(key: "StructuredVsUnstructured-2")
 **Watch it run:** the tea and milk brew *at the same time*, not one after the
 other. The whole thing takes about 0.5s (the longer of the two), not 0.8s. Both
 children are guaranteed to finish before the `Task` block ends; that's the
 "bound to a scope" guarantee. If you never wrote the `await`, Swift would still
 await them implicitly at the end of the scope; a structured child is never
 abandoned.

 * callout(Try it): Bump `steepTea`'s delay to `.milliseconds(900)`. Does the tray
 still arrive in about the *longer* of the two times, or the sum of both?

 Contrast that with the unstructured `Task { }` you've been using: it floats
 free. That freedom is the point sometimes (for instance, kicking off work from a
 SwiftUI button tap), but it means the safety net is gone. Nobody waits for it
 unless you do.

 [Previous](@previous)  |  page 4 of 11  |  [Next: Task Detached](@next)
 */
