# Rethinking Async

**URL:** <https://forums.swift.org/t/rethinking-async/37984>\
**Category:** Discussion\
**Tags:** concurrency\
**Created:** [June 29, 2020, 12:35pm UTC](https://forums.swift.org/t/rethinking-async/37984 "2020-06-29T12:35:50Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![Jon\_Hull](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jon_hull/32/678_2.png) [@Jon\_Hull](https://forums.swift.org/u/Jon_Hull)\
**Post date:** [June 29, 2020, 12:35pm UTC](https://forums.swift.org/t/rethinking-async/37984/1 "2020-06-29T12:35:51Z")

</div>

I have been reading the various threads on Async/Await, and I am coming around to the idea that we really need to rethink the problem entirely.

@Nevin pointed out that `await` on the call site isn't really helpful to the caller because having each line wait for the previous to complete is the standard way in which things work. We should only really be marking the case where we spin off a separate execution path... because that is the point at which we need to use different reasoning.

What if, instead of marking functions as `async`, we allow Types to be marked `async`:

```swift
let x: async Int

```

This looks a lot like a future, but has several important sugary nuances that allow it to fit much more naturally with the existing Swift control flow (avoiding having to chain things like .then{}).

The big change to the compiler would be augmenting `return` to be able to be used from a nested execution context, but in those cases, it would return an `async` variable (aka future) instead of the naked type:

```swift
func myFuncReturningAsync() -> async Int {
    myQueue.async{
         ///Do Calculation here in background
        return myInt ///This returns as an async Int
    }
}

```

There should be automatic promotion of variables to `async` versions when necessary (similar to optionals). For example, if there are multiple return points where some are `Int` and others are `async Int`, the non-async points are automatically promoted to `async`.

This means that an `Int` could be passed to an `async Int` parameter, but the opposite is not true. You would be forced to recover an actual `Int` (see below) to pass the value to an Int parameter.

To get back to the naked type, we would use `await`:

```swift
let x: async Int = myFuncReturningAsync()
let nakedX: Int = await x

```

Note: `await` would work similarly to `try` in that a single declaration would also await nested scopes, which allows for waiting on multiple variables to resolve.

We can also build functions into CGD (etc...) that take an async variable and call a closure on a queue when it has resolved:

```swift
myQueue.await(x) { nakedX in

}

```

One last bit of sugar for real world use. We should be able to await an array of async variables and get back an array of the naked types:

```swift
let myArray:[async Int] = ...
let nakedArray: [Int] = await myArray

```

So far it just looks like we have essentially sugared futures to avoid the usual boilerplate which makes them hard to read, and that is one possible implementation, but I want to point out that the compiler actually has a lot more freedom here to optimize things and implement in various ways. Await could still use coroutines, for example.

Let's use what we have so far to take things a step further and return to the idea of marking the function itself as `async`:

```swift
func myAsyncFunc() async -> Int {
    myQueue.async{
         ///Do Calculation here in background
        return myInt ///This still technically returns as an async Int
    }
}

```

Calling back to @Nevin's point, this should essentially work like any other function (avoiding the need to deal with it's asynchronous internals at all):

```swift
let nakedX = myAsyncFunc() 

```

which is essentially sugar for calling `await` on a version which returns `async Int`. That may seem strange/useless, but there is one last step. We can tell an async function to actually return that `async` version (aka don't await) by prefacing it with `async`:

```swift
let x = async myAsyncFunc()
let nakedX: Int = await x

```

So what does all this get us?

It gives us functions which:

1. Act like normal functions when called normally
2. Allow you to just return from a nested execution context when you have the resulting value and have it "just work"™
3. Allow you to easily treat them asynchronously when desired
4. Requires an annotation at the call site in the asynchronous case (which helps the caller to think through the implications... they are also forced by the compiler to `await` the value at some point in order to use it)
5. Allows easier reasoning about the execution context flow, since `await` always resolves in the current execution context
6. Have a natural syntax for passing/returning async variables as first class parameters

* * *

Let me give a concrete example of how this makes real world code simpler and easier to reason about. Grabbing an example from another thread, we would currently write:

```swift
func getImages(urlList: [URL], callback:(UIImage)->()) {
  myQueue.async {
    for url in urlList {
      fetchContents(of: url) {
        for imageURL in extractImageURLsFromHTML($0) {
          downloadImage(imageURL) {
            processImage($0) {
              callback($0)
            }
          }
        }
      }
    }
  }
}

let allImages:[UIImage] = //Getting this array left as an exercise for the reader

```

This could be rewritten as:

```swift
func getImages(urlList: [URL]) async -> [UIImage] {
  var images:[async UIImage] = []
  myQueue.async {
    for url in urlList {
      let contents = fetchContents(of: url)
      for imageURL in extractImageURLsFromHTML(contents) {
        let image = downloadImage(url)
        let processed = processImage(image)
        images.append(processed)
      }
    }
    return await images
  }
}

let allImages = getImages(urlList: urls)

```

Notice that we still need to use GCD to switch queues. `async` doesn't provide concurrency by itself... that is done by the internals of the async function. However, assuming all of these calls are `async` themselves (and can take async parameters), we can use their internal concurrency to make our function concurrent:

```swift
func imageURLs(in urlList: [URL]) async -> [URL] {
  return urlList.flatMap { url in
    let contents = async fetchContents(of: url)
    return async extractImageURLsFromHTML(contents)
  }
}

func getImage(url: URL) async -> UIImage {
  let image = async downloadImage(url)
  return async processImage(image)
}

func getImages(urlList: [URL]) async -> [UIImage] {
  return async imageURLs(in: urlList).map { async getImage($0) }
}

let allImages = getImages(urlList: urls)

```

Notice here how we are able to easily break up the function here into much simpler pieces using standard Swift mechanisms.

In conclusion, we get the benefits of futures, but we also get progressive disclosure that futures can't provide. These async functions behave like any other synchronous function to the caller, unless we specifically ask them to be asynchronous. When we get an asynchronous value, we can pass it around like a future, but we also get automatic promotion so we can pass in normal values just as easily.

I feel like this is much easier to reason about overall... especially for callers of the functions.

---

<div class="post-metadata">

**Author:** ![Lantua](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/lantua/32/8868_2.png) [@Lantua](https://forums.swift.org/u/Lantua)\
**Post date:** [June 29, 2020, 2:34pm UTC](https://forums.swift.org/t/rethinking-async/37984/2 "2020-06-29T14:34:56Z")

</div>

My first, and biggest concern, is that the most _natural_ usage of this design:

```swift
let a = asyncTask1()
let b = asyncTask2()

let c = await merge(a, b)

```

is the one that forces everything into one execution block. If this is in serialised context, e.g. main dispatch queue, it is definitely stalling until `asyncTask1`, and `asyncTask2` finishes.

* * *

> [@Jon\_Hull](#):
>
> The big change to the compiler would be augmenting `return` to be able to be used from a nested execution context

I don't see how we can achieve that syntax on its own. There has to be some bottom function that convert sync functions to async ones. And within that bottom function, we can pretty much escape the return block, so `return` doesn't really fit there.

More concretely, if I have, say, GCDAlpha, I don't see how I could make my own API to support that `return` syntax:

```swift
extension AlphaQueue {
  // Doesn't help even if `block` itself is `async`
  func async<T>(block: @escaping () -> T) async -> T {
    // What do I do now?
  }
}

```

* * *

> [@Jon\_Hull](#):
>
> One last bit of sugar for real world use. We should be able to await an array of async variables and get back an array of the naked types:
> 
> ```swift
> let myArray:[async Int] = ...
> let nakedArray: [Int] = await myArray
> 
> ```

I think `let nakedArray = myArray.map { await $0 }` would be about as clear without adding much complexity to the feature. You also have control over which items you're awaiting.

* * *

> [@Jon\_Hull](#):
>
> but I want to point out that the compiler actually has a lot more freedom here to optimize things and implement in various ways. Await could still use coroutines, for example.

I don't really see how this could work with coroutine. Could you please elaborate some more?

* * *

PS

> [@Jon\_Hull](#):
>
> We can also build functions into CGD (etc...) that take an async variable and call a closure on a queue when it has resolved:

Should be GCD there.

---

<div class="post-metadata">

**Author:** ![adamkemp](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/adamkemp/32/3017_2.png) [@adamkemp](https://forums.swift.org/u/adamkemp)\
**Post date:** [June 29, 2020, 5:04pm UTC](https://forums.swift.org/t/rethinking-async/37984/3 "2020-06-29T17:04:03Z")

</div>

> [@Jon\_Hull](#):
>
> @Nevin pointed out that `await` on the call site isn't really helpful to the caller because having each line wait for the previous to complete is the standard way in which things work.

I disagree with your entire premise. async/await, as implemented in other languages, solves a real problem. If you're brushing this off as "not really helpful" then you are either misunderstanding what it does or just not appreciating the benefits that you get from it. Other languages didn't go to so much trouble to implement this feature for a trivial improvement. It makes a significant difference in clarity and maintainability of asynchronous programming, which is a very common task in modern applications.

If you want to discuss new ideas to solve different problems then that's fine, but that's orthogonal to what async/await does, and it does not replace the need for async/await. Your proposal is not an alternative. It's a different feature to solve a different problem.

---

<div class="post-metadata">

**Author:** ![Lantua](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/lantua/32/8868_2.png) [@Lantua](https://forums.swift.org/u/Lantua)\
**Post date:** [June 29, 2020, 6:12pm UTC](https://forums.swift.org/t/rethinking-async/37984/4 "2020-06-29T18:12:45Z")

</div>

> [@adamkemp](#):
>
> If you want to discuss new ideas to solve different problems then that's fine, but that's orthogonal to what async/await does

Hmm? This one looks much more like C#'s async/await that the ones in the other thread even.

---

<div class="post-metadata">

**Author:** ![adamkemp](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/adamkemp/32/3017_2.png) [@adamkemp](https://forums.swift.org/u/adamkemp)\
**Post date:** [June 29, 2020, 6:28pm UTC](https://forums.swift.org/t/rethinking-async/37984/5 "2020-06-29T18:28:15Z")

</div>

If you want it to work like C# then you don't need async as a keyword at a call site or as a decorator for a type. That's trying to bake futures in as a language feature, which is not how C# does it, and not something I would recommend.

C#'s async/await works with _any_ futures type that conforms to a certain pattern of available methods (including extension methods). It doesn't tie you to just one built-in type, which is what this design appears to do.

---

<div class="post-metadata">

**Author:** ![Lantua](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/lantua/32/8868_2.png) [@Lantua](https://forums.swift.org/u/Lantua)\
**Post date:** [June 29, 2020, 6:58pm UTC](https://forums.swift.org/t/rethinking-async/37984/6 "2020-06-29T18:58:46Z")

</div>

I'm not saying it's doing exactly what C# is doing, but there are certainly similarities like letting async functions return a _checkpoint_ (`Task<Int>` & `async Int`) to be waited later with `async`, which is already a big departure from Chris' design.

---

<div class="post-metadata">

**Author:** ![John\_McCall](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/john_mccall/32/1351_2.png) [@John\_McCall](https://forums.swift.org/u/John_McCall)\
**Post date:** [June 29, 2020, 6:59pm UTC](https://forums.swift.org/t/rethinking-async/37984/7 "2020-06-29T18:59:05Z")

</div>

Respectfully, I don't think you need two threads for this same conversation. Do you want me to close the last one?

---

<div class="post-metadata">

**Author:** ![Lantua](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/lantua/32/8868_2.png) [@Lantua](https://forums.swift.org/u/Lantua)\
**Post date:** [June 29, 2020, 7:02pm UTC](https://forums.swift.org/t/rethinking-async/37984/8 "2020-06-29T19:02:06Z")

</div>

Shouldn't we wrap this one up, and go back to the other thread instead?

PS

It think there are three-ish; [Async/await status?](https://forums.swift.org/t/async-await-status/24026/), [An extensible Syntax for effects like e.g. async-await](https://forums.swift.org/t/an-extensible-syntax-for-effects-like-e-g-async-await/37443/), and this.

---

<div class="post-metadata">

**Author:** ![John\_McCall](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/john_mccall/32/1351_2.png) [@John\_McCall](https://forums.swift.org/u/John_McCall)\
**Post date:** [June 29, 2020, 7:04pm UTC](https://forums.swift.org/t/rethinking-async/37984/9 "2020-06-29T19:04:47Z")

</div>

I'll let the three of you come to a consensus, but please pick one.

---

<div class="post-metadata">

**Author:** ![Lantua](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/lantua/32/8868_2.png) [@Lantua](https://forums.swift.org/u/Lantua)\
**Post date:** [June 29, 2020, 7:10pm UTC](https://forums.swift.org/t/rethinking-async/37984/10 "2020-06-29T19:10:35Z")

</div>

I think we could close this one, and we all can go back to [Async/await status?](https://forums.swift.org/t/async-await-status/24026/), until the design ends up being more fleshed out.

---

<div class="post-metadata">

**Author:** ![QuinceyMorris](https://avatars.discourse-cdn.com/v4/letter/q/d26b3c/32.png) [@QuinceyMorris](https://forums.swift.org/u/QuinceyMorris)\
**Post date:** [June 29, 2020, 7:28pm UTC](https://forums.swift.org/t/rethinking-async/37984/11 "2020-06-29T19:28:21Z")

</div>

Respectfully, I think @Jon_Hull has started a different conversation, and it's not the conversation happening in any other currently active thread. @Jon_Hull is proposing a _ **different** _ solution to the underlying problem, and it's a direction I very much want to discuss.

What would be good is if we could agree to keep discussion of the implementation details of _ **other** _ proposed solutions out of this thread. Those belong in their respective in-progress threads.

---

<div class="post-metadata">

**Author:** ![John\_McCall](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/john_mccall/32/1351_2.png) [@John\_McCall](https://forums.swift.org/u/John_McCall)\
**Post date:** [June 29, 2020, 7:39pm UTC](https://forums.swift.org/t/rethinking-async/37984/12 "2020-06-29T19:39:15Z")

</div>

These are not the async/await discussion forums. You don't need separate threads for every new idea. When we have multiple threads for the same broad topic, time and time again what we see is that the same people end up saying similar things in all the threads and it becomes very difficult for other people to follow and participate. This is already a very high-traffic conversation; the participants are writing collectively writing tens of thousands of words a day. The sheer volume makes it unapproachable.

Therefore, there is going to be one active conversation thread from here on out. I am happy to merge threads or close them as the participants like, but if I don't get clear signal, I will just do what I think is best. This is your opportunity to shape the outcome.

---

<div class="post-metadata">

**Author:** ![Jon\_Hull](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jon_hull/32/678_2.png) [@Jon\_Hull](https://forums.swift.org/u/Jon_Hull)\
**Post date:** [June 29, 2020, 7:53pm UTC](https://forums.swift.org/t/rethinking-async/37984/13 "2020-06-29T19:53:37Z")

</div>

FWIW, I agree with @QuinceyMorris. The reason I started a new thread is that although some of the terms are the same, I am actually talking about a completely different approach and way of thinking about the issue. I feel like if I had posted this there, it would have been rejected/ignored for being off-topic.

That said, I will defer to your judgment. I hope this much needed conversation can actually happen somewhere though...

---

<div class="post-metadata">

**Author:** ![QuinceyMorris](https://avatars.discourse-cdn.com/v4/letter/q/d26b3c/32.png) [@QuinceyMorris](https://forums.swift.org/u/QuinceyMorris)\
**Post date:** [June 29, 2020, 7:59pm UTC](https://forums.swift.org/t/rethinking-async/37984/14 "2020-06-29T19:59:02Z")

</div>

I acknowledge that your position is reasonable, and I share your frustration about the nature of the discussion.

However, there are valid counterarguments to the rationale you've expressed:

1. If you try to constrain multiple discussions to one thread, the effect is that any one discussion is constantly being derailed by others, and no one can follow the topic anyway.

2. If a discussion gets too long, newcomers can't afford to invest the time to read the whole history, which result in a lot of repetition of ideas that have already been discussed.

So, I think a better model is to allow new discussions to start, and let them die away after a few days — which is what has actually happened to a number of these async/await topics, including ones that I've started.

Specifically, though, if you're asking for input about what threads to close, I say close those that have \>70 posts (or somewhere around there). Experience has shown that longer threads don't really go anywhere.

---

<div class="post-metadata">

**Author:** ![Lantua](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/lantua/32/8868_2.png) [@Lantua](https://forums.swift.org/u/Lantua)\
**Post date:** [June 29, 2020, 8:14pm UTC](https://forums.swift.org/t/rethinking-async/37984/15 "2020-06-29T20:14:27Z")

</div>

Actually, I don't have a strong opinion which thread it's gonna be.

---

<div class="post-metadata">

**Author:** ![John\_McCall](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/john_mccall/32/1351_2.png) [@John\_McCall](https://forums.swift.org/u/John_McCall)\
**Post date:** [June 30, 2020, 4:21am UTC](https://forums.swift.org/t/rethinking-async/37984/16 "2020-06-30T04:21:38Z")

</div>

Okay. Since it seems to be people's preference, and since there are links to the earlier threads, we can continue in this one.

---

<div class="post-metadata">

**Author:** ![SwiftUI](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/swiftui/32/24361_2.png) [@SwiftUI](https://forums.swift.org/u/SwiftUI)\
**Post date:** [June 30, 2020, 5:44am UTC](https://forums.swift.org/t/rethinking-async/37984/17 "2020-06-30T05:44:07Z")

</div>

OK, currently we have two categories of ideas for `async/await` to tackle both asynchronous and concurrency problems.

1. async routine/function coroutine for async operation
2. async T/value/result for concurrency - parallel async

I think 1) already well solved by traditional `async/await` pattern both in swift and other similar traits of lang C#/JS...

Maybe this thread `Rethinking` is focused on parallel async or concurrency representation by async/await pattern.

assuming `func asyncFunc(...) async -> T`

```swift
async let result1 : T = await asyncFunc(...) 
async let result2 : T = await asyncFunc(...) 
/* define async result but not call/branch async func immediately - 
`lazy async` or Future/Promise async representation */

await result1, result2 //actually call the asyncFunc and wait/suspend for return in parallel directly 

```

`or` call `asyncFunc(...) async -> Void` Actions in parallel

```swift
await asyncFunc(...), asyncFunc(...), ... //parallel async

```

Conclusion, await `async Value` for concurrency async, get async results in parallel; and await `async Func` for traditional async get async results in sequence.

_Alternative pattern_: using `ViewBuilder` equivalent `AsyncBuilder {...}` eDSL to build async calls in parallel but this is out of this topic scope.

---

<div class="post-metadata">

**Author:** ![Karl](https://avatars.discourse-cdn.com/v4/letter/k/34f0e0/32.png) [@Karl](https://forums.swift.org/u/Karl)\
**Post date:** [June 30, 2020, 11:50am UTC](https://forums.swift.org/t/rethinking-async/37984/18 "2020-06-30T11:50:00Z")

</div>

> [@adamkemp](#):
>
> async/await, as implemented in other languages, solves a real problem. If you're brushing this off as "not really helpful" then you are either misunderstanding what it does or just not appreciating the benefits that you get from it. Other languages didn't go to so much trouble to implement this feature for a trivial improvement

It’s certainly in async/await’s favour that it’s been adopted by so many languages. Having so many developers familiar with the concept makes it valuable in its own right.

That said, popularity alone is not enough to justify adding async/await to Swift. Many of those implementations have holes and sharp edges that I think we can improve on. See the discussion about `modify` accessors and how we’d like to express handling coroutine cancellation in the language, where many others simply acknowledge that it’s a dangerous trapdoor and continue.

There are other language designs for composing asynchronous tasks in to pipelines which I think could be just as powerful (if not more so) than async/await as we see it in popular languages today.

---

<div class="post-metadata">

**Author:** ![Lantua](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/lantua/32/8868_2.png) [@Lantua](https://forums.swift.org/u/Lantua)\
**Post date:** [June 30, 2020, 11:54am UTC](https://forums.swift.org/t/rethinking-async/37984/19 "2020-06-30T11:54:13Z")

</div>

I see a lot of people mentioning designs that doesn’t immediately `await` function calls to facilitate concurrency. Fair enough.

What I don’t understand is the claim that it’d just work with coroutine. Could someone elaborate more on that. Or do they just treat `async Int` as native `Future` wrapper?

---

<div class="post-metadata">

**Author:** ![Chris\_Lattner3](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/chris_lattner3/32/348_2.png) [@Chris\_Lattner3](https://forums.swift.org/u/Chris_Lattner3)\
**Post date:** [June 30, 2020, 9:32pm UTC](https://forums.swift.org/t/rethinking-async/37984/20 "2020-06-30T21:32:48Z")

</div>

@John_McCall said:

> With respect, I'd encourage people to think of Chris's old paper less as the "current design" and more as a conceptual starting point. It's trying to get us all familiar with some of the basic problems and make sure we're using the same terms to talk about them. If Chris's intrinsics don't let us express what we need to express, well, we need different intrinsics, that's all. In this case, `beginAsync` needs to also take some description of the current execution context that will get passed down to the `async` function so that it can resume itself there if need be. Generally this intrinsic would get used in the implementation of e.g. `DispatchQueue.async` , which can very easily describe the queue to `beginAsync` after moving execution there.

I agree. I think there are several different issues that are getting conflated into "async await":

- There is a well understood effect system and state machine transformation that is enabled with the `async` and `await` keywords. There is general cross language and industry alignment about what these do and why, and LLVM has increasingly good support for this (thanks to John and others).

- There are Swift standard library functions that are required for converting between the effect and other ways of modeling the same concepts. These would take low level forms and may have high level equivalents (like a "future" type). These are precedented in things like `withoutActuallyEscaping(:do:)` and `Result` for the `@escaping` and `throws` systems, respectively.

- We probably want higher level APIs and patterns for dealing with cancelation and timeouts across async domains. There was a recent twitter thread with Joe Groff and others on it discussing [this blog post](https://trio.readthedocs.io/en/stable/tutorial.html) and [this library](https://vorpus.org/blog/timeouts-and-cancellation-for-humans/) both of which are really well written and motivated, and have ideas we should be inspired by IMO.

- There is a discussion about what is the underlying execution model for threads.

My point is that these are all separable discussions and orthogonal features - the results of which should compose in a beautiful way, but this is a "manifesto" level discussion just for async/await, not to mention actors and all the other stuff in the concurrency manifesto I wrote 3 years ago - time flies!

-Chris

[Next page](https://forums.swift.org/t/rethinking-async/37984.md?page=2)
