[swift-evolution] [Concurrency] modifying beginAsync, suspendAsync to support cancellation

Marc Schlichte marc.schlichte at googlemail.com
Sat Aug 19 13:33:17 CDT 2017


Hi,

to support cancellation, I propose the following changes to `beginAsync()` and `suspendAsync()`:

`beginAsync()` returns an object adhering to a `Cancelable` protocol:

```
func beginAsync(_ body: () async throws-> Void) rethrows -> Cancelable

protocol Cancelable { func cancel() }
```

`suspendAsync()` takes a new thunk parameter:

```
func suspendAsync<T>(onCancel: () -> Void, body: (cont: (T) -> Void, err: (Error) -> Void) async -> T 
```

Now, when `cancel()` is invoked, the `onCancel` thunk in the current suspension (if any) will be called.


Example:

```
var task: Cancelable?

@IBAction func buttonDidClick(sender: AnyObject) {
  task = beginAsync {
    do {
      let image = try await processImage()
      imageView.image = image 
    } catch AsyncError.canceled {
      imageView.image = nil // or some fallback image...
    } catch {
      // other handling
    }
  }  
)

@IBAction func cancelDidClick(sender: AnyObject) {
  task?.cancel()
}

func processImage() async throws -> UIImage {
  // This processing should be on a background queue (or better an Actor :-) - but ignored for this example
  var cancelled = false
  suspendAsync(onCancel: {
    cancelled = true
  }, body: { cont, err in
     while !done && !cancelled {
       // do the processing on image until done or canceled
     }
     guard !cancelled else { err(AsyncError.canceled) } // BTW, maybe change signature of `suspendAsync` to allow to throw here instead
     cont(image)
  }
}
```

Cheers
Marc



More information about the swift-evolution mailing list