/*:#localized(key: "CompletionOrder")
 # Results arrive in completion order

 Here's the part that trips people up. A task group hands you results **in the
 order children finish**, not the order you added them. If frame 7 calibrates
 faster than frame 2, frame 7's result comes out first. Let's make that visible by
 giving each frame a different, random duration.
 */
//#-editable-code
import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

// Each frame now finishes after a different, unpredictable delay.
func loadFrame(_ index: Int) async -> (index: Int, brightness: Double) {
    let delay = Double.random(in: 0.1...0.9)
    try? await Task.sleep(for: .seconds(delay))
    return (index, Double(index) * 1.5)
}

Task {
    await withTaskGroup(of: (index: Int, brightness: Double).self) { group in
        for index in 1...6 {
            group.addTask { await loadFrame(index) }
        }

        for await frame in group {
            print("Frame \(frame.index) arrived")
        }
    }
    print("All frames in.")
    PlaygroundPage.current.finishExecution()
}
//#-end-editable-code
/*:#localized(key: "CompletionOrder-2")
 **Watch it run:** the "Frame N arrived" lines print in a scrambled order that
 changes every run: maybe `4, 1, 5, 2, 6, 3`, maybe something else. This ordering
 is **nondeterministic**: it depends entirely on which child happens to finish
 first. Never write code that assumes the first result out of a group belongs to
 the first task you added.

 * callout(Try it): Narrow the delay to `Double.random(in: 0.1...0.15)`; the
 arrivals bunch up closer to submission order, but they're still not guaranteed.

 [Previous](@previous)  |  page 4 of 9  |  [Next: Putting the Order Back](@next)
 */
