Feedback was very positive on the concept of adding async/await in general with a few key points raised:
It was suggested that try await reads better than await try. The core team agrees, and the proposal will be modified accordingly.
There was some discussion of alternatives to async (such as suspends) which may better describe the meaning. The core team feels that the benefit of sticking with async as a term of art with precedent in many other languages was preferable to the the slight descriptive benefit of alternative names. Note that other uses of async such as async let will be considered in other proposals.
Several reviewers expressed concern that it was hard to review this proposal "stand alone", since it interacts so closely with, and its use depends on, other yet-to-be-reviewed proposals. The core team acknowledges this, but feels that this is unavoidable given the large surface area of the whole concurrency feature. To mitigate this, reviewers of subsequent proposals should feel free to revisit parts of accepted concurrency proposals in reviewing those subsequent proposals when they interact.
Several reviewers were disappointed about subsetting out getters. The core team wants to be clear that this is just left as a future direction, not ruled out, and as such isn't a reason to hold back on accepting this proposal.
In a separate thread to the review, there was some discussion of the necessity of try and await. The core team does not believe the current requirement to mark throwing calls with try should be revisited, and thinks there is a similar need to mark possible suspension points with await. The core team would be open to considering future proposals that allow multiple calls needing either try or await to be sugared somehow (for example, some form of try block).
This is an important first step on our concurrency journey. To help navigate how this proposal relates to expected future proposals, this diagram should be of help:
Thanks to everyone who participated in the review!
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.
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.
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...
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.
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()
}
}
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.