Reputation: 6130
I'm trying to use async/await with Swift 5.5. I have my async function, but whenever I try to call it, I get this error:
'async' call in a function that does not support concurrency
Here's the code sample:
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()
let result = await tryThis.getSomethingLater(3.141592653589793238462)
print("result: \(result)")
What's the solution for this??
Upvotes: 44
Views: 48590
Reputation: 32163
When calling async
code (including actor
fields) from non-async
code, you have to wrap it in a Task
:
Task {
let result = await tryThis.getSomethingLater(3.141592653589793238462)
print("result: \(result)")
}
If you need it to escape that async context, you can use traditional callbacks:
func awaitedResult(callback: @escaping (String) -> Void) {
Task {
let result = await tryThis.getSomethingLater(3.141592653589793238462)
callback(result)
}
}
awaitedResult { result in
print("result: \(result)")
}
Swift by Sundell also offers some alternatives involving Combine
Further reading: Concurrency — The Swift Programming Language (Swift 5.5)
Upvotes: 20
Reputation: 66
This error occurs when you’ve tried to call an async function from a synchronous function, which is not allowed in Swift – asynchronous functions must be able to suspend themselves and their callers, and synchronous functions simply don’t know how to do that.
func doAsyncWork() async {
print("Doing async work")
}
func doRegularWork() {
Task {
await doAsyncWork()
}
}
doRegularWork()
Upvotes: 1
Reputation: 6130
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.
Upvotes: 69
Reputation: 2510
In my case I just refactored the:
async { some-code-which-can-take-some-time }
with:
Task { some-code-which-can-take-some-time }
Upvotes: 2
Reputation: 7698
The original error is produced because "top-level" await is not yet supported in Swift (that is, code at the global scope).
The error just means you need to provide the async function with an asynchronous context, like using Task
, Task.detached
or a TaskGroup
, depending on the behaviour you want.
Task.detached {
let result = await tryThis.getSomethingLater(3.141592653589793238462)
print("result: \(result)")
}
However, your code snippet should work in the future when top-level await is eventually proposed, implemented and supported. Top-level await support was mentioned as part of the original proposal for async/await SE-0296.
Upvotes: 5