<div dir="ltr">
<p class="MsoNormal">I can’t speak to the more low-level implications, but to the
extent this is essentially “syntactic sugar for completion handlers,” I can
base my opinion on making iOS apps in different contexts for about 5 years. (Long
enough to remember when Objective-C Blocks as completion handlers were the great new thing that was
going to perfectly solve all our problems). <span></span>I especially appreciated the clear goals section of the proposal, which I thought was on-target.</p>
<p class="MsoNormal"><span> </span></p>
<p class="MsoNormal">I have some uncertainty
about how async/await will work in practice—literally, how to use it. Some of
this is just needing to see many more examples and examples in common scenarios
at the site of use and in the creation of async functions. <span></span></p>
<p class="MsoNormal"><span> </span></p>
<p class="MsoNormal">Most of the questions I had I answered myself in the course
of writing this up, but I include them for confirmation and to call attention to aspects that may not be clear. (I also tend to use block and closure interchangeably when it reads better, especially where I learned them as objective-C completion blocks, please do correct if necessary.) I hope the write up will provide some clarity for others (assuming it's basically correct or correctly labeled where it may not be) and provide feedback on where it could be more clear. </p><p class="MsoNormal"> </p>
<p class="MsoNormal"><u>Starting at <font face="monospace, monospace">beginAsync</font><span></span></u></p><p class="MsoNormal"><font face="arial, helvetica, sans-serif"><br></font></p><p class="MsoNormal"><span style="font-family:Menlo;font-size:11px;color:rgb(4,51,255)">@IBAction</span><span style="color:rgb(0,0,0);font-family:Menlo;font-size:11px"> </span><span style="font-family:Menlo;font-size:11px;color:rgb(4,51,255)">func</span><span style="color:rgb(0,0,0);font-family:Menlo;font-size:11px"> buttonDidClick(sender:</span><span style="font-family:Menlo;font-size:11px;color:rgb(52,149,175)">AnyObject</span><span style="color:rgb(0,0,0);font-family:Menlo;font-size:11px">) {</span><br></p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)"> <span style="color:rgb(0,143,0)">// 1</span></p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)"> beginAsync {</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)"> <span style="color:rgb(0,143,0)">// 2</span></p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)"> <span style="color:rgb(4,51,255)">let</span> image = await processImage()</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)"> imageView.image = image</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)"> }</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)"> <span style="color:rgb(0,143,0)">// 3</span></p><p class="MsoNormal">
</p><p style="margin:0px;font-size:11px;line-height:normal;color:rgb(0,0,0)"><span style="font-family:Menlo">} </span></p>
<p class="MsoNormal">After entering a <font face="monospace, monospace">beginAsync</font> block, code will continue to
execute inside the block (like <font face="monospace, monospace">do</font> blocks now) until it encounters an <font face="monospace, monospace">await</font><font face="arial, helvetica, sans-serif">-marked</font> function. In this sense it’s more <font face="monospace, monospace">beginAsyncContext</font> not begin-being-asynchronous, which is how it could be read. Although the end of this block will be
where synchronicity will resume once execution goes async (suspends?) inside it. <span></span></p>
<p class="MsoNormal"><span> </span></p>
<p class="MsoNormal">At the point of an await function, two things can happen: 1)
If the function needs time to execute -- if "it suspends" is how to describe it? -- code execution will
jump to outside the <font face="monospace, monospace">beginAsync</font> block. 2) If the function has its result
already, code execution will continue inside the block, or jump to catch error if it exists?
(This would not be different from now in that a receiver of a completion block
can invoke it immediately, before returning). <span></span></p>
<p class="MsoNormal"><span> </span></p>
<p class="MsoNormal">This is important to clarify because after the async block
(or any functions with completion blocks now) code afterwards can’t assume what
might have happened. But this proposal doesn’t solve the problem seen in the
<font face="monospace, monospace">@IBAction</font> example, of order 1, 3, 2, which is actually worse because if you can
have immediate completion you aren’t sure if it is 1, 3, 2, or 1, 2, 3. This is actually an easy enough situation to handle if you’re aware of it.<span></span></p>
<p class="MsoNormal"><span> </span></p>
<p class="MsoNormal"><u>Use of <font face="monospace, monospace">suspendAsync</font></u><span></span></p><p class="MsoNormal"><u><font face="monospace, monospace"><br></font></u></p>
<p class="MsoNormal"><font face="monospace, monospace">suspendAsync</font> is the point at which the waiting is actually
triggered? For a while this threw me at first (no pun intended). Since it looks like the
more common transitive verb form of “suspend”, I read this as
“suspending-the-async”, therefore, resuming. The primitives looked like beginAsync started doing something asynchronous and suspendAsync resumed synchronous execution? Maybe the better order would be <font face="monospace, monospace">asyncSuspend</font> (paired with <font face="monospace, monospace">asyncBegin</font>--or even better for that, <font face="monospace, monospace">asyncContext</font>) would be less likely to
be confused this way? (More on the primitives just below)<span></span></p>
<p class="MsoNormal"> <span></span></p>
<p class="MsoNormal">However, even inside the block passed to <font face="monospace, monospace">suspendAsync</font> the code is not asynchronous yet. The code in the block
passed to <font face="monospace, monospace">suspendAsync</font> is still executed at the time the block is passed in. That
code is responsible for taking the continuation block and error block, escaping
with them and storing them somewhere, and invoking either when the value or
error is ready. Is it correct that those blocks will be called on thread that <font face="monospace, monospace">suspendAsync</font> was called on? <span></span></p>
<p class="MsoNormal"><span> </span></p>
<p class="MsoNormal">It was also somewhat unclear what happens then the block passed to <font face="monospace, monospace">suspendAsync</font> reaches the end. Since <font face="monospace, monospace">suspendAsync</font> is itself an <font face="monospace, monospace">async</font> function called with <font face="monospace, monospace">await</font>, it looks like control now passes back to the end
of the original <font face="monospace, monospace">beginAsync</font> block, wherever that is. That the <font face="monospace, monospace">getStuff() async</font> wrapper example returns the
result of the call to <font face="monospace, monospace">suspendAsync</font> in one line obscured what was going on. That was</p><p class="MsoNormal"><br></p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)"><span style="color:rgb(4,51,255)">func</span> getStuff() async -> Stuff {</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)"> <span style="color:rgb(4,51,255)">return</span> await suspendAsync { continuation <span style="color:rgb(4,51,255)">in</span></p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)"> getStuff(completion: continuation)</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)"> }</p><p class="MsoNormal">
</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)">}</p><p class="MsoNormal"><br></p><p class="MsoNormal">What's going on would be more clear over two lines. For example if we wanted to do further processing after getting our async result before returning: </p><p class="MsoNormal"><span></span></p>
<p class="MsoNormal"><span> </span></p>
<p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)"><span style="color:rgb(4,51,255)">func</span> getStuff() async -> Stuff {</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)"> <span style="color:rgb(4,51,255)">let</span> rawStuff = await suspendAsync { continuation <span style="color:rgb(4,51,255)">in</span></p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)"> getStuff(completion: continuation)</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)"> }</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)"> <span style="color:rgb(4,51,255)">return</span> processed(rawStuff)</p><p class="MsoNormal" style="background-color:rgb(246,248,250)">
</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)">}</p>
<p class="MsoNormal"><span style="font-family:'Times New Roman'"><span> </span></span></p>
<p class="MsoNormal">Where exactly execution is paused and will resume is more
clear. </p><p class="MsoNormal"><br></p><p class="MsoNormal">In fact, the show the full <font face="monospace, monospace">async/await</font> life cycle, it’s possible to demonstrate in the same scope
before introducing the semantics of async functions: <span></span></p>
<p class="MsoNormal"><span> </span></p>
<p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo"><font color="#0000ff">beginAsync</font><span style="color:rgb(0,0,0)"> {</span></p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)"> <span style="color:rgb(4,51,255)">do</span> {</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo"><span style="color:rgb(0,0,0)"> <span style="color:rgb(4,51,255)">let</span> stuff = <span style="color:rgb(4,51,255)">try</span> </span><font color="#0000ff">await</font><font color="#000000"> suspendAsync { continuation, error </font><span style="color:rgb(4,51,255)">in</span></p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,143,0)"><span style="color:rgb(0,0,0)"> </span>//perform long-running task on other queue then call continuation, error as appropriate</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)"> }</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,143,0)"><span style="color:rgb(0,0,0)"> </span>//Continuation block resumes here with `stuff`</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)"> doSomething(with: stuff)</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)"> } <span style="color:rgb(4,51,255)">catch</span> {</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,143,0)"><span style="color:rgb(0,0,0)"> </span>//error block resumes here</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)"> handleGettingStuffError(error)</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)"> }</p><p class="MsoNormal">
</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)">}</p>
<p class="MsoNormal"><span> </span></p>
<p class="MsoNormal">This is correct? While as the comments state it may be true that eventually many
users won’t need to interact with <font face="monospace, monospace">suspendAsync</font> (though I think <font face="monospace, monospace">beginAsync</font> will remain common, such as the <font face="monospace, monospace">@IBAction</font> example) it’s familiar the key
method that breaks familiar procedural execution and creates the blocks that will
allow it to resume. During the transition it will be especially important for
those adapting their own or others’ code. It should be prominent.</p><p class="MsoNormal"><span></span></p>
<p class="MsoNormal"><span> </span></p>
<p class="MsoNormal">One opinion point that I do want to mention though about the last
example: There should probably be just one <font face="monospace, monospace">suspendAsync</font> method, the one with an error continuation. First, if there's the no-error version then that will probably proliferate in examples (seen already) coders will get learn how to use <font face="monospace, monospace">await/async</font> ignoring errors, a
habit we’ll have to break later. But if there's only one suspend method, then <font face="monospace, monospace">await</font> would always include <font face="monospace, monospace">try</font>, forcing programmers who want to ignore them to have
an empty <font face="monospace, monospace">catch</font> block (or use the space for a comment with your legitimate
reason!). An empty/comment only catch block would also be the case for legacy APIs with no completion error parameter, though maybe these could be imported as throwing <font face="monospace, monospace">Error.nil</font> or <font face="monospace, monospace">Error.false</font>, etc. So all uses would look like <font face="monospace, monospace">beginAsync { … } catch { … }</font>. </p><p class="MsoNormal"><br></p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)">beginAsync {</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo"><font color="#000000"> </font><span style="color:rgb(4,51,255)">let</span><font color="#000000"> stuff = </font><font color="#0000ff">await</font><span style="color:rgb(0,0,0)"> suspendAsync { continuation, error <span style="color:rgb(4,51,255)">in</span></span></p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,143,0)"><span style="color:rgb(0,0,0)"> </span>//perform long-running task on other queue then call continuation, error as appropriate</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)"> }</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,143,0)"><span style="color:rgb(0,0,0)"> </span>//Continuation block resumes here with `stuff`</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)"> doSomething(with: stuff)</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(4,51,255)"><span style="color:rgb(0,0,0)">} </span>catch<span style="color:rgb(0,0,0)"> {</span></p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,143,0)"><span style="color:rgb(0,0,0)"> </span>//error block resumes here</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)"> handleGettingStuffError(error)</p><p class="MsoNormal">
</p><p style="margin:0px;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)">}</p><p class="MsoNormal"><br></p><p class="MsoNormal">I don’t lightly suggest trying to use syntax
to force good habits, but in this case it would be the cleaner API. The alternative
is forcing code that does handle errors to look like the last example above, obviously more ungainly than code that ignores errors.
Handling errors is already too easy to ignore. <span></span></p>
<p class="MsoNormal"><span> </span></p>
<p class="MsoNormal">There are more substantive points that I want to touch on
later, largely from my use of a promise/future frameworks as part of a
production app that went really well, even when working with UIKit,
AppDelegate, NSNotification. (I also worked on a project where the last guy had
rolled his own promises/futures system; you can guess how that went.) <span></span></p>
<p class="MsoNormal"><span> </span></p>
<p class="MsoNormal">But I wanted to clarify the basics of use. Thanks for this proposal and everyone’s comments.<span></span></p>
<p class="MsoNormal"><span> </span></p>
<p class="MsoNormal">Mike Sanderson <span></span></p>
</div><div class="gmail_extra"><br><div class="gmail_quote">On Fri, Aug 18, 2017 at 5:09 PM, Adam Kemp via swift-evolution <span dir="ltr"><<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div style="word-wrap:break-word;line-break:after-white-space">Thanks for the quick response!<br><div><span class=""><br><blockquote type="cite"><div>On Aug 18, 2017, at 1:15 PM, Chris Lattner <<a href="mailto:clattner@nondot.org" target="_blank">clattner@nondot.org</a>> wrote:</div><br class="m_-7029954908496862768Apple-interchange-newline"><div><div style="word-wrap:break-word">On Aug 18, 2017, at 12:34 PM, Adam Kemp <<a href="mailto:adam.kemp@apple.com" target="_blank">adam.kemp@apple.com</a>> wrote:<br><div><blockquote type="cite">For instance, say you’re handling a button click, and you need to do a network request and then update the UI. In C# (using Xamarin.iOS as an example) you might write some code like this:<br><div><div style="word-wrap:break-word;line-break:after-white-space"><div><br></div><blockquote style="margin:0 0 0 40px;border:none;padding:0px"><div>private async void HandleButtonClick(object sender, EventArgs e) {</div><div> var results = await GetStuffFromNetwork();</div><div> UpdateUI(results);</div><div>}</div></blockquote><div><br></div>This event handler is called on the UI thread, and the UpdateUI call must be done on the UI thread. The way async/await works in C# (by default) is that when your continuation is called it will be on the same synchronization context you started with. That means if you started on the UI thread you will resume on the UI thread. If you started on some thread pool then you will resume on that same thread pool.<br></div></div></blockquote><div><br></div><div>I completely agree, I would love to see this because it is the most easy to reason about, and is implied by the syntax. I consider this to be a follow-on to the basic async/await proposal - part of the Objective-C importer work, as described here:</div><div><a href="https://gist.github.com/lattner/429b9070918248274f25b714dcfc7619#fix-queue-hopping-objective-c-completion-handlers" target="_blank">https://gist.github.com/<wbr>lattner/<wbr>429b9070918248274f25b714dcfc76<wbr>19#fix-queue-hopping-<wbr>objective-c-completion-<wbr>handlers</a></div></div></div></div></blockquote><div><br></div></span><div>Maybe I’m still missing something, but how does this help when you are interacting only with Swift code? If I were to write an asynchronous method in Swift then how could I do the same thing that you propose that the Objective-C importer do? That is, how do I write my function such that it calls back on the same queue?</div><div><br></div><div>In my mind, if that requires any extra effort then it is already more error prone than what C# does.</div><span class=""><br><blockquote type="cite"><div style="word-wrap:break-word"><div><div><br></div><blockquote type="cite"><div><div style="word-wrap:break-word;line-break:after-white-space"><div>Another difference between the C# implementation and this proposal is the lack of futures. While I think it’s fair to be cautious about tying this proposal to any specific futures implementation or design, I feel like the value of tying it to some concept of futures was somewhat overlooked. For instance, in C# you could write a method with this signature:</div></div></div></blockquote>...<br><blockquote type="cite"><div><div style="word-wrap:break-word;line-break:after-white-space"><div><br></div><div>The benefit of connecting the async/await feature to the concept of futures is that you can mix and match this code freely. The current proposal doesn’t seem to allow this. </div></div></div></blockquote><div><br></div><div>The current proposal provides an underlying mechanism that you can build futures on, and gives an example. As shown, the experience using that futures API would work quite naturally and fit into Swift IMO.</div></div></div></blockquote><br></span></div><div>I feel like this is trading conceptual complexity in order to gain compiler simplicity. What I mean by that is that the feature feels harder to understand, and the benefit seems to be that this feature can be used more generally for other things. I’m not sure that’s a good tradeoff.</div><div><br></div><div>The other approach, which is to build a specific async/await feature using compiler transformations, may be less generic (yield return would have to work differently), but it seems (to me) easier to understand how to use.</div><div><br></div><div>For instance, this code (modified from the proposal):</div><div><br></div><blockquote style="margin:0 0 0 40px;border:none;padding:0px"><div>@IBAction func buttonDidClick(<wbr>sender:AnyObject) { </div><div> doSomethingOnMainThread();</div><div><div> beginAsync {</div></div><div> let image = await <wbr>processImage()</div><div><div> imageView.image = image</div></div><div><div> }</div></div><div> doSomethingElseOnMainThread();</div><div>}</div></blockquote><br><div>Is less straightforward than this:</div><div><br></div><blockquote style="margin:0 0 0 40px;border:none;padding:0px"><div>@IBAction async func buttonDidClick(sender:<wbr>AnyObject) {</div><div> doSomethingOnMainThread();</div><div> let imageTask = processImage()</div><div> doSomethingElseOnMainThread();</div><div> imageView.image = await imageTask</div><div>}</div></blockquote><br><div>It’s clearer from reading of the second function what order things will run in. The code from the proposal has a block of code (the callback from beginAsync) that will run in part before the code that follows, but some of it will run after buttonDidClick returns. That’s confusing in the same way that callbacks in general are confusing. The way that async/await makes code clearer is by making it more WYSIWYG: the order you see the code written in is the order in which that code is run. The awaits just mark breaks.</div></div><br>______________________________<wbr>_________________<br>
swift-evolution mailing list<br>
<a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a><br>
<a href="https://lists.swift.org/mailman/listinfo/swift-evolution" rel="noreferrer" target="_blank">https://lists.swift.org/<wbr>mailman/listinfo/swift-<wbr>evolution</a><br>
<br></blockquote></div><br></div>