Search code examples
swiftconcurrencyswift5swift-concurrency

How to await x seconds with async await Swift 5.5


How do I use the new Swift 5.5 await keyword to wait for a duration of time?

Normally, with completion handlers, you would have something like this by using DispatchQueue's asyncAfter(deadline:execute:):

func someLongTask(completion: @escaping (Int) -> Void) {
    DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
        completion(Int.random(in: 1 ... 6))
    }
}

someLongTask { diceRoll in
    print(diceRoll)
}

How can this be converted to using async & await in Swift 5.5?


Solution

  • iOS 16+ / macOS 13+

    There's a newer API, sleep(for:tolerance:clock:), used like so:

    // 3 seconds
    try await Task.sleep(for: .seconds(3))
    

    iOS <16 / macOS <13

    You can use Task.sleep(nanoseconds:) to wait for a specific duration. This is measured in nanoseconds, not seconds.

    Here's an example:

    func someLongTask() async -> Int {
        try? await Task.sleep(nanoseconds: 1 * 1_000_000_000) // 1 second
        return Int.random(in: 1 ... 6)
    }
    
    Task {
        let diceRoll = await someLongTask()
        print(diceRoll)
    }
    

    It may be easier to use an extension for sleep so you can just pass in seconds:

    extension Task where Success == Never, Failure == Never {
        static func sleep(seconds: Double) async throws {
            let duration = UInt64(seconds * 1_000_000_000)
            try await Task.sleep(nanoseconds: duration)
        }
    }
    

    Which would now be called like so:

    try await Task.sleep(seconds: 1)
    

    Note that sleep is called with try. An error is thrown if the sleep is cancelled. If you don’t care if it’s cancelled, just try? is fine.