[swift-evolution] [Concurrency] modifying beginAsync, suspendAsync to support cancellation
Marc Schlichte
marc.schlichte at googlemail.com
Mon Aug 28 18:42:32 CDT 2017
> Am 19.08.2017 um 20:33 schrieb Marc Schlichte via swift-evolution <swift-evolution at swift.org>:
>
> 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()
> }
Just adding here that instead of directly using the low-level `beginAsync`, a Future/Promise could be used instead:
```
var task: Future<UIImage>?
@IBAction func buttonDidClick(sender: AnyObject) {
task = Future {
try await processImage()
}
do {
imageView.image = try await task!.get()
} catch AsyncError.canceled {
imageView.image = nil // or some fallback image...
} catch {
// other handling
}
}
@iBAction func cancelDidClick(sender: AnyObject) {
task?.cancel()
}
```
Of course, the init of Future would have to be changed
convenience init(_ body: () throws async -> T) {
self.init()
task = beginAsync {
do {
self.fulfill(try await body())
} catch {
self.fail(error)
}
}
}
(BTW also added missing throws and try in code above)
and `cancel()` would have to be added to `Future`:
```
public func cancel() {
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)
> }
> }
> ```
^ BTW, this should be `return await suspendAsync(…`
>
> Cheers
> Marc
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170829/388422b2/attachment.html>
More information about the swift-evolution
mailing list