How to Await X Seconds with Async Await Swift 5.5

How to await x seconds with async await Swift 5.5

iOS 16+ / macOS 13+

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

// 3 seconds
try await Task.sleep(until: .now + .seconds(3), clock: .continuous)


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.

Async/Await function without a return | Swift 5.5

This is what worked the best:

try await withUnsafeThrowingContinuation { (continuation: UnsafeContinuation<Void, Error>) in

Swift 5.5 async let - error: expression is 'async' but is not marked with 'await'

Building as a macOS command line project (Xcode: File -> New -> Project, then select "Command Line Tool" from the macOS tab), the code works perfectly. (This was a suggestion from a response in the Apple Developer Forums.)

I've added a single sleep(10) to the end so that the command line tool doesn't exit before the async calls finish:

import Foundation

print("Hello, world!")

func doIt() async -> String {
let t = TimeInterval.random(in: 0.25 ... 2.0)
Thread.sleep(forTimeInterval: t)
return String("\(Double.random(in: 0...1000))")
}

async {
async let a = doIt()
async let b = doIt()
async let c = doIt()
async let d = doIt()

let results = await [a, b, c, d]
for result in results {
print(" \(result)")
}
}
sleep(10)

This produces the expected sort of console output (Note: the actual values will differ on every run)*:

Hello, World!
415.407747869283
574.28639828183
689.4706625185836
385.56539085197113
Program ended with exit code: 0

Problem making API request with async / await in swift 5.5

Cancellation errors for async URL session data tasks occur when the parent task is cancelled, for example if MyView is removed from the view hierarchy. Is this what is happening?

How to call async function asynchronously without awaiting for the result

This behavior will change depending upon the context.

  • If you invoke this from a non-isolated context, then first and second will run on separate threads. In this scenario, the second task is not actually waiting for the first task, but rather there is a race as to which will finish first. This can be illustrated if you do something time-consuming in the first task and you will see the second task is not waiting at all.

    This introduces a race between first and second and you have no assurances as which order they will run. (In my tests, it runs second before first most of the time, but it can still occasionally run first before second.)

  • However, if you invoke this from an actor-isolated context, then first will wait for second to yield before running.

So, the question is, do you really care which order these two tasks start? If so, you can eliminate the race by (obviously) putting the Task { await first() } after the call to second. Or do you simply want to ensure that second won’t wait for first to finish? In that case, this already is the behavior and no change to your code is required.


You asked:

What if await first() needs to be run on the same queue as second() but asynchronously. … I am just thinking [that if it runs on background thread that it] would mean crashes due to updates of UI not from the main thread.

You can mark the routine to update the UI with @MainActor, which will cause it to run on the main thread. But note, do not use this qualifier with the time-consuming task, itself (because you do not want to block the main thread), but rather decouple the time-consuming operation from the UI update, and just mark the latter as @MainActor.

E.g., here is an example that manually calculates π asynchronously, and updates the UI when it is done:

func startCalculation() {
Task {
let pi = await calculatePi()
updateWithResults(pi)
}
updateThatCalculationIsUnderway() // this really should go before the Task to eliminate any races, but just to illustrate that this second routine really does not wait
}

// deliberately inefficient calculation of pi

func calculatePi() async -> Double {
await Task.detached {
var value: Double = 0
var denominator: Double = 1
var sign: Double = 1
var increment: Double = 0

repeat {
increment = 4 / denominator
value += sign * 4 / denominator
denominator += 2
sign *= -1
} while increment > 0.000000001

return value
}.value
}

func updateThatCalculationIsUnderway() {
statusLabel.text = "Calculating π"
}

@MainActor
func updateWithResults(_ value: Double) {
statusLabel.text = "Done"
resultLabel.text = formatter.string(for: value)
}

Note: To ensure this slow synchronous calculation of calculatePi is not run on the current actor (presumably the main actor), we want an “unstructured task”. Specifically, we want a “detached task”, i.e., one that is not run on the current actor. As the Unstructured Concurrency section of The Swift Programming Language: Concurrency: Tasks and Task Groups says:

To create an unstructured task that runs on the current actor, call the Task.init(priority:operation:) initializer. To create an unstructured task that’s not part of the current actor, known more specifically as a detached task, call the Task.detached(priority:operation:) class method.

Swiftui async / await with Firebase

You are calling getDocuments with a completion handler.

You cannot mix Swift structured concurrency async/await directly with getDocuments / completion handler. async/await and completion handler are opposites.

Everything about your completion handler is wrong. You cannot throw from a completion handler. You cannot return anything from a completion handler. That's the whole point of async/await. That is why async/await supersedes completion handlers.

To wrap async/await around a completion handler call, you must use withUnsafeThrowingContinuation.

async' call in a function that does not support concurrency swift ios Xcode async/await

The answer here is that the "await getSomethingLater" must be called from an async context. Literally that means changing this:

let result = await tryThis.getSomethingLater(3.141592653589793238462)
print("result: \(result)")

into this:

Task {
let result = await tryThis.getSomethingLater(3.141592653589793238462)
print("result: \(result)")
}

So the whole thing becomes:

class TryThis {
func getSomethingLater(_ number: Double) async -> String {
// test - sleep for 3 seconds, then return
Thread.sleep(forTimeInterval: 3)
return String(format: ">>>%8.2f<<<", number)
}
}

let tryThis = TryThis()

Task {
let result = await tryThis.getSomethingLater(3.141592653589793238462)
print("result: \(result)")
}

Here's the output:

result: >>> 3.14<<<

There's great info in the Meet async/await in Swift video from WWDC21.

How to trigger action after x seconds in swiftUI

Create a delay, which then sets the @State property hasTimeElapsed to true when the time has elapsed, updating the view body.

With Swift 5.5 and the new concurrency updates (async & await), you can now use task(_:) like the following:

struct ContentView: View {
@State private var hasTimeElapsed = false

var body: some View {
Text(hasTimeElapsed ? "Sorry, too late." : "Please enter above.")
.task(delayText)
}

private func delayText() async {
// Delay of 7.5 seconds (1 second = 1_000_000_000 nanoseconds)
try? await Task.sleep(nanoseconds: 7_500_000_000)
hasTimeElapsed = true
}
}

See more info about Task.sleep(nanoseconds:) in this answer.



Older versions

Xcode 13.0+ now supports concurrency, backwards compatible! However, here is still the 'old' way to do it:

You can use DispatchQueue to delay something. Trigger it with onAppear(perform:) (which happens when the view first appears). You could also hook the delay up to a Button instead if wanted.

Example:

struct ContentView: View {
@State private var hasTimeElapsed = false

var body: some View {
Text(hasTimeElapsed ? "Sorry, too late." : "Please enter above.")
.onAppear(perform: delayText)
}

private func delayText() {
// Delay of 7.5 seconds
DispatchQueue.main.asyncAfter(deadline: .now() + 7.5) {
hasTimeElapsed = true
}
}
}

How to create a delay in Swift?

Instead of a sleep, which will lock up your program if called from the UI thread, consider using NSTimer or a dispatch timer.

But, if you really need a delay in the current thread:

do {
sleep(4)
}

This uses the sleep function from UNIX.



Related Topics



Leave a reply



Submit