<div dir="ltr"><br><br><div class="gmail_quote"><div dir="ltr">Em ter, 12 de set de 2017 às 16:48, Adam Kemp via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a>&gt; escreveu:<br></div><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"><div><br><blockquote type="cite"><div style="word-wrap:break-word"><div><blockquote type="cite"><div><div class="m_-8826152709756124442Singleton" style="word-wrap:break-word"><div>I think that decision makes sense for try/throws, but I feel like the await keyword is fundamentally different from that. The pitfalls of not understanding how the code is transformed and how it will behave at runtime are much greater with await than with try.</div><div><br></div><div>If you have a line of code with multiple expressions that can throw then the consequences of not knowing which particular one threw the error are minor. In most cases it doesn’t matter, and you would handle a given error the same regardless of which subexpression threw the error.</div><div><br></div><div>With await the function is actually broken up into pieces, and unrelated code can run in between those pieces on the same thread and/or the same queue. That has a much higher potential of leading to subtle bugs if you can’t see where those breaks are.</div></div></div></blockquote></div><br><div>What sort of bugs?  Can you please provide a concrete example we can discuss?</div></div></blockquote></div><br></div><div style="word-wrap:break-word;line-break:after-white-space"><div>Here’s just a simple example of code that looks right but is buggy:</div><div><br></div><blockquote style="margin:0 0 0 40px;border:none;padding:0px"><div>@IBAction func buttonDidClick(sender:AnyObject) {</div><div>    beginAsync {</div><div>        let image = await processImage(downloadImage(), resize: self.resizeSwitch.isOn)</div><div>        displayImage(image)</div><div>    }</div><div>}</div></blockquote><br><div>That code I believe would be equivalent to this more explicit code:</div><div><br></div><blockquote style="margin:0 0 0 40px;border:none;padding:0px"><div><div>@IBAction func buttonDidClick(sender:AnyObject) {</div><div>    beginAsync {</div><div>        let downloadedImage = await downloadImage()</div><div>        let resizeSwitchIsOn = self.resizeSwitch.isOn</div><div>        let image = await processImage(downloadedImage, resize: resizeSwitchIsOn)</div><div>        displayImage(image)</div><div>    }</div><div>}</div></div></blockquote><br><div>The subtlety here is that control can be returned to the run loop before the code checks the value of resizeSwitch.isOn. That means there is a time when the user can change the switch before it’s read.</div><div> <br></div></div></blockquote><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"><div></div><div>Obviously someone could write the second version of this code and have the same bug so the problem isn’t that it’s possible to write this bug. The problem is that it’s not clear in the first example where those breaks are where control may be returned to the run loop. Someone reading the first example can’t tell when self.resizeSwitch.isOn is going to be read (now or some future iteration of the run loop).</div><div><br></div></div></blockquote><div><br></div><div><div>So, the problem is a predefined order to evaluate the values, not a &quot;second&quot; await.</div></div><div><br></div><div>Like you said, the person can write the wrong code ever anyway!</div><div> <br></div><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"><div></div><div>The correct way to write this would be to read the UI up front:</div><div><br></div><blockquote style="margin:0 0 0 40px;border:none;padding:0px"><div>@IBAction func buttonDidClick(sender:AnyObject) {</div><div>    beginAsync {</div><div>        let resizeSwitchIsOn = self.resizeSwitch.isOn</div><div>        let downloadedImage = await downloadImage()</div><div>        let image = await processImage(downloadedImage, resize: resizeSwitchIsOn)</div><div>        displayImage(image)</div><div>    }</div><div>}</div></blockquote><div><br></div></div></blockquote><div><br></div><div>In this case is not better discuss the precedence order to evaluate the values?<br></div><div> </div><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"><div></div><div>Based on my experience dealing with async/await code and the subtlety of returning to the run loop in between expressions I think the added clarity of an explicit await for each call outweighs the inconvenience. It is a hard enough adjustment for people to understand that this function executes in pieces with other code running in between. I think it would be an even harder adjustment if you couldn’t use a simple rule like “every time you see await there is a break right there”. In the original example there are two breaks in the same line, and it’s opaque to the reader where those breaks are.</div><div><br></div></div></blockquote><div><br></div><div>Precedence order still be a problem using another &quot;await&quot; keyword.</div><div> </div><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"><div></div><div>This example also shows once again the importance of returning to the right queue. If the “await downloadImage” continues on some other queue then you would be using UIKit on a background thread, which is not allowed. It seems like we’re starting to see some convergence on this idea, which I’m glad to see.</div></div></blockquote><div><br></div><div><br></div><div>The proposal already covered this:</div><div><br></div><div><pre style="box-sizing:border-box;font-family:SFMono-Regular,Consolas,&quot;Liberation Mono&quot;,Menlo,Courier,monospace;font-size:13.6px;margin-top:0px;margin-bottom:0px;word-wrap:normal;padding:16px;overflow:auto;line-height:1.45;background-color:rgb(246,248,250);border-radius:3px;word-break:normal;color:rgb(36,41,46)">  await DispatchQueue.<span class="inbox-inbox-pl-smi" style="box-sizing:border-box">main</span>.<span class="inbox-inbox-pl-c1" style="box-sizing:border-box;color:rgb(0,92,197)">syncCoroutine</span>()
</pre><br class="inbox-inbox-Apple-interchange-newline"></div><div>Maybe not the best way, but this is one possible way.</div><div><br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">_______________________________________________<br>
swift-evolution mailing list<br>
<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a><br>
<a href="https://lists.swift.org/mailman/listinfo/swift-evolution" rel="noreferrer" target="_blank">https://lists.swift.org/mailman/listinfo/swift-evolution</a><br>
</blockquote></div></div>