joshmori
joshmori

Reputation: 492

Swift: cannot call async func from normal func?

In Swift 5.5:

Following Swift Code will get compiler error: "async call in a function that does not support concurrency".

// Swift
func hi() async {
    print("hi")
}

func callAsyncHi() {
    hi()
}

In Javascript, await is only valid in async function, but calling an async func from a normal func is valid. So following Javascript Code prints "hi" asynchronously.

// Javascript
async function hi() {
    console.log("hi")
}

function callAsyncHi() {
    hi()
}

callAsyncHi()

I also found an async func cannot be called without await or async let. Why Swift is designed in this way?

Upvotes: 6

Views: 6910

Answers (2)

Soumya Mahunt
Soumya Mahunt

Reputation: 2802

You can invoke async function from normal function by wrapping the asynchronous call inside a task:

func hi() async {
    print("hi")
}

func callAsyncHi() {
    let task = Task {
        await hi()
    }
}

This is similar to how you can invoke async methods in java script from synchrnous context, key difference being in Swift you have to explicitly specify it and inside Task you can invoke more than one async methods.

There are certain reasons behind a design like this. Key reason being in Swift all async methods are associated with tasks and support cooperative cancellation.

Swift concurrency uses a cooperative cancellation model. Each task checks whether it has been canceled at the appropriate points in its execution, and responds to cancellation in whatever way is appropriate.

By requiring async keyword Swift keeps track of the dependency chain of async calls and propagates cancellation event when it occurs (While as far as I know promises in JS doesn't support cancellation of promises). In the above code, you can invoke cancellation on task variable by keeping track of it.

Now the second reason, more important in my opinion, is by requiring explicit invocation this makes it easier to review code as I have to check if cancellation for such invocations are properly handled while in case of JS it is harder to pinpoint such misuse.

Upvotes: 12

joshmori
joshmori

Reputation: 492

  • An async function from Javascript returns a Promise.
  • An async function from C# returns a Task.

We pass Promises/Tasks around freely and do many things.

But an async function from Swift returns ???.

??? is hidden. I cannot even print its type.

// swift compiler error: Expression is 'async' but is not marked with 'await'
func callAsyncHi() {
  Task {
    async let tmp = hi()
    print(Mirror(reflecting: tmp).subjectType)
    await tmp
  }
}

I guess there is no ???. So we have to use async func with await.

Upvotes: -4

Related Questions