drewster
drewster

Reputation: 6130

'async' call in a function that does not support concurrency

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

Answers (5)

Ky -
Ky -

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

Kishan Raiyani
Kishan Raiyani

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()

Reference here

Upvotes: 1

drewster
drewster

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

Ali Nawaz
Ali Nawaz

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

Bradley Mackey
Bradley Mackey

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

Related Questions