<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><br class=""><div><blockquote type="cite" class=""><div class="">Am 19.08.2017 um 20:33 schrieb Marc Schlichte via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>&gt;:</div><br class="Apple-interchange-newline"><div class=""><div class="">Hi,<br class=""><br class="">to support cancellation, I propose the following changes to `beginAsync()` and `suspendAsync()`:<br class=""><br class="">`beginAsync()` returns an object adhering to a `Cancelable` protocol:<br class=""><br class="">```<br class="">func beginAsync(_ body: () async throws-&gt; Void) rethrows -&gt; Cancelable<br class=""><br class="">protocol Cancelable { func cancel() }<br class="">```<br class=""><br class="">`suspendAsync()` takes a new thunk parameter:<br class=""><br class="">```<br class="">func suspendAsync&lt;T&gt;(onCancel: () -&gt; Void, body: (cont: (T) -&gt; Void, err: (Error) -&gt; Void) async -&gt; T <br class="">```<br class=""><br class="">Now, when `cancel()` is invoked, the `onCancel` thunk in the current suspension (if any) will be called.</div></div></blockquote><blockquote type="cite" class=""><div class=""><div class=""><br class=""><br class="">Example:<br class=""><br class="">```<br class="">var task: Cancelable?<br class=""><br class="">@IBAction func buttonDidClick(sender: AnyObject) {<br class=""> &nbsp;task = beginAsync {<br class=""> &nbsp;&nbsp;&nbsp;do {<br class=""> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;let image = try await processImage()<br class=""> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;imageView.image = image <br class=""> &nbsp;&nbsp;&nbsp;} catch AsyncError.canceled {<br class=""> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;imageView.image = nil // or some fallback image...<br class=""> &nbsp;&nbsp;&nbsp;} catch {<br class=""> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// other handling<br class=""> &nbsp;&nbsp;&nbsp;}<br class=""> &nbsp;} &nbsp;<br class="">)<br class=""><br class="">@IBAction func cancelDidClick(sender: AnyObject) {<br class=""> &nbsp;task?.cancel()<br class="">}<br class=""></div></div></blockquote><div><br class=""></div><div>Just adding here that instead of directly using the low-level `beginAsync`, a Future/Promise could be used instead:</div><div><br class=""></div><div>```</div><div>var task: Future&lt;UIImage&gt;?</div><div><br class=""></div><div>@IBAction func buttonDidClick(sender: AnyObject) {</div><div>&nbsp; task = Future {</div><div>&nbsp; &nbsp; try await processImage()</div><div>&nbsp; }</div><div>&nbsp; do {</div><div>&nbsp; &nbsp; imageView.image = try await task!.get()</div><div>&nbsp; } catch AsyncError.canceled {</div><div>&nbsp; &nbsp; imageView.image = nil // or some fallback image...</div><div>&nbsp; } catch {</div><div>&nbsp; &nbsp; // other handling</div><div>&nbsp; }</div><div>}</div><div><br class=""></div><div>@iBAction func cancelDidClick(sender: AnyObject) {</div><div>&nbsp; task?.cancel()</div><div>}</div><div>```</div><div><br class=""></div><div>Of course, the init of Future would have to be changed</div><div><br class=""></div><div><pre style="box-sizing: border-box; font-family: SFMono-Regular, Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 13.600000381469727px; margin-top: 0px; margin-bottom: 0px; line-height: 1.45; word-wrap: normal; padding: 16px; overflow: auto; background-color: rgb(246, 248, 250); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; word-break: normal; color: rgb(36, 41, 46);" class="">  <span class="pl-k" style="box-sizing: border-box; color: rgb(215, 58, 73);">convenience</span> <span class="pl-k" style="box-sizing: border-box; color: rgb(215, 58, 73);">init</span>(<span class="pl-en" style="box-sizing: border-box; color: rgb(111, 66, 193);">_</span> <span class="pl-smi" style="box-sizing: border-box;">body</span>: () throws async <span class="pl-k" style="box-sizing: border-box; color: rgb(215, 58, 73);">-&gt;</span> T) {
    <span class="pl-c1" style="box-sizing: border-box; color: rgb(0, 92, 197);">self</span>.<span class="pl-k" style="box-sizing: border-box; color: rgb(215, 58, 73);">init</span>()
    task = beginAsync {
      <span class="pl-k" style="box-sizing: border-box; color: rgb(215, 58, 73);">do</span> {
        <span class="pl-c1" style="box-sizing: border-box; color: rgb(0, 92, 197);">self</span>.<span class="pl-c1" style="box-sizing: border-box; color: rgb(0, 92, 197);">fulfill</span>(try await <span class="pl-c1" style="box-sizing: border-box; color: rgb(0, 92, 197);">body</span>())
      } <span class="pl-k" style="box-sizing: border-box; color: rgb(215, 58, 73);">catch</span> {
        <span class="pl-c1" style="box-sizing: border-box; color: rgb(0, 92, 197);">self</span>.<span class="pl-c1" style="box-sizing: border-box; color: rgb(0, 92, 197);">fail</span>(error)
      }
    }
  }</pre><div class="">(BTW also added missing throws and try in code above)</div><div class=""><br class=""></div><div class="">and `cancel()` would have to be added to `Future`:</div><div class=""><br class=""></div><div class="">```</div><div class="">public func cancel() {</div><div class="">&nbsp; task?.cancel()</div><div class="">}</div><div class=""><br class=""></div><div class="">```</div></div><div class=""><br class=""></div><blockquote type="cite" class=""><div class=""><div class=""><br class="">func processImage() async throws -&gt; UIImage {<br class=""> &nbsp;// This processing should be on a background queue (or better an Actor :-) - but ignored for this example<br class=""> &nbsp;var cancelled = false<br class=""> &nbsp;suspendAsync(onCancel: {<br class=""></div></div></blockquote></div><div><blockquote type="cite" class=""><div class=""><div class="">&nbsp; &nbsp;cancelled = true<br class=""> &nbsp;}, body: { cont, err in<br class=""> &nbsp;&nbsp;&nbsp;&nbsp;while !done &amp;&amp; !cancelled {<br class=""> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// do the processing on image until done or canceled<br class=""> &nbsp;&nbsp;&nbsp;&nbsp;}<br class=""> &nbsp;&nbsp;&nbsp;&nbsp;guard !cancelled else { err(AsyncError.canceled) } // BTW, maybe change signature of `suspendAsync` to allow to throw here instead<br class=""> &nbsp;&nbsp;&nbsp;&nbsp;cont(image)<br class=""> &nbsp;}<br class="">}<br class="">```<br class=""></div></div></blockquote><div><br class=""></div>^ BTW, this should be `return &nbsp;await suspendAsync(…`<br class=""><blockquote type="cite" class=""><div class=""><div class=""><br class="">Cheers<br class="">Marc<br class=""><br class="">_______________________________________________<br class="">swift-evolution mailing list<br class=""><a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a><br class="">https://lists.swift.org/mailman/listinfo/swift-evolution<br class=""></div></div></blockquote></div><br class=""></body></html>