[Accepted with Modification] SE-0296: async/await

The Core Team felt that implicitly suspending at break/continue/return/throw/fall-off-the-end was too subtle. They/we are open to lifting this restriction in the future, but there should be more discussion around making this less subtle.

Doug

5 Likes

It is still not clear to me whether the proposal includes a way for async functions to directly give up their thread without awaiting another async function.

In the proposed solution section, the proposal text states:

think of an asynchronous function as an ordinary function that has the special power to give up its thread. Asynchronous functions don’t typically use this power directly;

By saying async functions don’t “typically” give up their thread directly, the reader is left with the impression that they can do so.

Is that in fact part of the proposal?

No, this is Task.yield() in the structured concurrency proposal.

Doug

2 Likes

Thanks

This certainly sounds more natural. For consistency's sake, I hope function signatures will be changed from async throws to throws async —is this correct? I imagine that it will be hard to keep track of which keyword comes first otherwise.

On the other hand, that leaves async let in maybe an odd position...

1 Like

You could think of it as inner-to-outer, because the awaiting happens before the throwing, in which case the ordering makes sense.

11 Likes

You can remember it because it's mirrored:

func f() async throws -> String
//   (1) (2)   (3)   (4) (5)
let string: String = try await f()
//          (5)   (4)(3) (2)   (1)
43 Likes

You may also consider Typed throws (WIP), by which having async throws ErrorType may be more eye-pleasing than throws ErrorType async.

5 Likes

These all sound like good reasons. Thanks for the clarification!

1 Like

This is great, just one question:
The fact that the proposal is "accepted" means that this will be included in swift 5.4 already?

It does not mean that.

1 Like

Unlikely. My prediction is Swift 6 @ WWDC.

1 Like

Swift 6 is very far away, there're at least 5.4-spring and 5.5-fall releases in 2021 between next milestone maybe 2022 for swift 6. I think concurrency feature set phase1 will be part of 5.4 and phase 2 finished in 5.5.

1 Like

Let's not speculate about the release time of new features, even if they are in sight.

14 Likes

I hope it won't. That would mean it is declared stable and can't be changed anymore before we are able to test the whole stack more widely.

For such feature, the more time we got before freezing it, the better.

8 Likes

Something I'm missing in this whole thing is just what the internals of the async function look like. I see a lot of examples of calling async functions using await, but I didn't see how the function did something internally (perhaps calling a network API) and then returned a value.

func loadWebResource() async -> Data {
    makeAPICall { result in
        // how to return function value here??? Is it as simple as...
        return result
    }
}

Or to put it another way, how do I write my own async functions?

I believe the details of that will be worked out in the structured concurrency proposal, which hasn't yet been put up for review. But currently you can implement async functions using the withUnsafeContinuation and withUnsafeThrowingContinuation apis.

Here's an example for performing a network request that works on the latest Swift toolchain:

struct RequestError: Error {
    var error: Error?
    var response: URLResponse?
}

func request(_ request: URLRequest, session: URLSession = .shared) async throws -> (Data, URLResponse) {
    await try withUnsafeThrowingContinuation { continuation in
        let task = session.dataTask(with: request) { data, response, error in
            if let data = data, let response = response {
                continuation.resume(returning: (data, response))
            } else {
                continuation.resume(throwing: RequestError(error: error, response: response))
            }

        }
        task.resume()
    }
}
2 Likes

This question has come up consistently a sufficient number of times that I would suggest that the authors might consider incorporating text into the proposal to address it, even if it is just pointing to a future proposal. It seems to be key to user understanding of what's being proposed.

8 Likes

Great example. Thanks. I wasn't sure if the compiler was magically going to reach into the function and find return values or what.

This syntax is very similar to that used in RxSwift when doing something like Single.create { }.

Although, true to form, it's pretty deep into standardSwiftRunOnSentence-land (withUnsafeThrowingContinuation indeed!).

Umm... if you don't mind my asking, why is the continuation considered unsafe? Because it may never be called?

The api docs have this line:

The operation functions must resume the continuation exactly once .

This is up to the consumers of the api and isn't enforced by the compiler, which I would assume is why they are labeled as unsafe.

3 Likes