<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=""><div class="">Maybe this will be handled more gracefully via the actor model.</div><div class=""><br class=""></div><div class="">1. if you're calling from an actor, you'd be called back on its internal queue. If you're calling from the 'main actor' (or just the main thread), you'd be called back on the main queue</div><div class="">2. you would just add a cancel() method to the actor</div><div class="">3. the processImage() method would call suspendAsync() and store the continuation block in an array, once the result is available it would call all the continuation blocks with the result</div><div class=""><br class=""></div><div class="">That would only work if we're able to send messages to the actor while previous messages to processImage() are pending. That's something that's not yet clear to me if actors can work that way.</div><div class=""><br class=""></div><div class="">Thomas</div><br class=""><div><blockquote type="cite" class=""><div class="">On 19 Aug 2017, at 13:56, Jakob Egger via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>&gt; wrote:</div><br class="Apple-interchange-newline"><div class=""><meta http-equiv="Content-Type" content="text/html charset=utf-8" class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class="">I've read async/await proposal, and I'm thrilled by the possibilities. Here's what I consider the canonical example:<div class=""><pre class=""><code class="">@IBAction func buttonDidClick(sender:AnyObject) {
  beginAsync {
    let image = await processImage()
    imageView.image = image
  }
}</code></pre></div><div class="">This is exactly the kind of thing I will use async/await for!</div><div class=""><br class=""></div><div class="">But while this example looks extremely elegant, it would suffer from a number of problems in practice:</div><div class=""><br class=""></div><div class="">1. There is no guarantee that you are on the main thread after `await processImage()`<br class="">2. There is no way to cancel processing&nbsp;<br class="">3. Race Condition: If you click the button a second time before `processImage()` is done, two copies will run simultaneously and you don't know which image will "win".<br class=""><br class=""></div><div class="">So I wondered: What would a more thorough example look like in practice? How would I fix all these issues?</div><div class=""><br class=""></div><div class="">After some consideration, I came up with the following minimal example that addresses all these issues:</div><div class=""><pre class=""><code class="">class ImageProcessingTask {
  var cancelled = false
  func process() async -&gt; Image? { … }
}
</code></pre><pre class=""><code class="">var currentTask: ImageProcessingTask?
@IBAction func buttonDidClick(sender:AnyObject) {
<div class="">&nbsp; currentTask?.cancelled = true</div><div class="">&nbsp; let task = ImageProcessingTask()</div><div class="">&nbsp; currentTask = task</div>  beginAsync {
    guard let image = await task.process() else { return }
    DispatchQueue.main.async {
      guard task.cancelled == false else { return }
      imageView.image = image
&nbsp;   }
  }
}</code></pre><div class="">If my example isn't obvious, I've documented my thinking (and some alternatives) in a gist:</div></div><div class=""><a href="https://gist.github.com/jakob/22c9725caac5125c1273ece93cc2e1e7" class="">https://gist.github.com/jakob/22c9725caac5125c1273ece93cc2e1e7</a></div><div class=""><br class=""></div><div class=""><div class="">Anyway, this more realistic code sample doesn't look nearly as nice any more, and I actually think this could be implemented nicer without async/await:</div></div><div class=""><br class=""></div><div class=""><div class=""><font face="monospace" class=""><span style="white-space: pre;" class="">class ImageProcessingTask {</span></font></div><div class=""><font face="monospace" class=""><span style="white-space: pre;" class="">&nbsp; var cancelled = false</span></font></div><div class=""><font face="monospace" class=""><span style="white-space: pre;" class="">&nbsp; func process(completionQueue: DispatchQueue, completionHandler: (Image?)-&gt;()) { … }</span></font></div><div class=""><font face="monospace" class=""><span style="white-space: pre;" class="">}</span></font></div><div class=""><font face="monospace" class=""><div class=""><span style="white-space: pre;" class="">@IBAction func buttonDidClick(sender:AnyObject) {</span></div><span style="white-space: pre;" class="">&nbsp; currentTask?.cancelled = true<br class="">&nbsp; let task = ImageProcessingTask()<br class=""></span><div class=""><span style="white-space: pre;" class="">&nbsp; currentTask = task</span></div><div class=""><span style="white-space: pre;" class="">&nbsp; task.process(</span><span style="white-space: pre;" class="">completionQueue: </span><span style="white-space: pre;" class="">DispatchQueue.main</span><span style="white-space: pre;" class="">) { (image) in</span></div></font><font face="monospace" class=""><span style="white-space: pre;" class="">&nbsp; &nbsp; guard let&nbsp;</span></font><span style="font-family: monospace; white-space: pre;" class="">image = </span><span style="font-family: monospace; white-space: pre;" class="">image else { return }</span></div><div class=""><span style="font-family: monospace; white-space: pre;" class="">    guard task.cancelled == false else { return }</span></div><div class=""><font face="monospace" class=""><span style="white-space: pre;" class="">    </span></font><span style="font-family: monospace; white-space: pre;" class="">imageView.image = image</span><font face="monospace" class=""><span style="white-space: pre;" class=""><br class="">&nbsp; }<br class=""></span><div class=""><span style="white-space: pre;" class="">}</span></div><span style="white-space: pre;" class=""><br class=""></span></font><div class="">So I wonder: What's the point of async/await if it doesn't result in nicer code in practice? How can we make async/await more elegant when calling from non-async functions?</div></div></div><div class=""><br class=""></div><div class=""><br class=""></div><div class="">Jakob</div><div class=""><br class=""></div></div>_______________________________________________<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></blockquote></div><br class=""></body></html>