[swift-evolution] Async Affordances Wish List

Marc Schlichte marc.schlichte at googlemail.com
Fri Aug 11 15:03:14 CDT 2017


Hi,

to help with async programming tasks, I could see the following affordances on async/await to be helpful:

1. Make existing Continuation Passing Style (CPS) APIs available via async/await.

2. Await with timeouts.

3. Possibility to cancel awaits.


To be more specific:


Ad 1. 

I think it could be helpful if an API like: 

`func foo(_ arg: Arg, completion: (val: Val) -> Void)`

could be invoked like this:

`let val: Val = await foo(arg)`

(i.e, having a mapped signature like: `func foo(_ arg: Arg) async -> Val`)


Often, the completion takes an error argument:

`func bar(_ arg: Arg, completion: (val: Val?, err: Error?) -> Void)`

Calling it via async/await might look like this:
```
do {
  let val: Val = try await bar(arg)
} catch {
  // handle error
}
```

I could imagine that new type attributes are needed to make this happen:

@continuation: the closure which continues the async control flow

`func bar(_ arg: Arg, completion: @continuation (val: Val?, err: Error?) -> Void)`

This function would transform to something like this:

`func bar(_ arg: Arg) throws async -> Val`


Some developers already have adopted a Result style - it would be nice to support the same conversion here too:

`func bar2(_ arg: Arg, completion: @continuation (result: Result<Val>) -> Void)` ->
`func bar2(_ arg: Arg) throws async -> Val`

Maybe `Result` has to adopt a specific protocol to allow this.


Ad 2 - Await with Timeouts

It could be helpful to have the option to specify how long an await should wait before throwing a timeout error:

```
do {
  let val = try await(for: 2.718) bar(arg)
} catch AsyncError.timeout {
  // handle timeout
} catch {
  // handle other errors
}
```


Ad 3 - Cancelation

Sometimes, I maybe want to cancel an async operation.

```
class C {
  var task: CancelableTask<Val>?
  
  func startProcess() {
    do {
      task = baz(arg)
      let val = try await task!
      …
    } catch AsyncError.cancelation {
      // handle cancelation
    } catch {
      // handle other errors
    }
  }

  func cancelProcess() {
    task?.cancel()
  }
}
```

To be cancelable, a function needs to support that:
```
func baz(_ arg: Arg) -> CancelableTask<Val> {
    let task = CancelableTask<Val>()
    let sess = URLSession.dataTask(with: arg.url) { data, resp, err in
      // No automatic CPS conversion here as dataTask does not return Void
      task.finish(with: data.val)
    }
    task.onCancellation {
      sess.cancel()
    }
    return task
} 
```

`baz` would have an equivalent signature of:

`func baz(_ arg: Arg) throws async cancelable-> Val`

Then, the non-cancellable `bar` function from above could also be written as:

`func bar(_ arg: Arg) -> Task<Val>` (aka. Promise, Future, …)

and the non-throwing `foo` function:

`func foo(_ arg: Arg) -> NonThrowingTask<Val>`


Of course, work on concurrency in Swift has a much bigger scope than this laundry list of
 on async/await capabilities.

Cheers
Marc



-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170811/d24c7227/attachment.html>


More information about the swift-evolution mailing list